Skip to content

Instantly share code, notes, and snippets.

@stevenpollack
Last active June 8, 2016 18:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevenpollack/b066b13b6d4df41dd831795db72e89db to your computer and use it in GitHub Desktop.
Save stevenpollack/b066b13b6d4df41dd831795db72e89db to your computer and use it in GitHub Desktop.
React notes
// React components must extend the React.Component Class
// and therefore expose a render method, which returns
// JSX (JavaScript XML).
// JSX looks exactly like unquoted HTML, except that
// - the HTML 'class' attribute is referenced via 'className'
// - we can mix in javascript via code blocks enclosed in {}
// - we can send arguments ('props' in React) to components:
// + props are accessed via the `this.props` object
// - we can access state with (`this.state`)
class Comment extends React.Component {
render () {
return(
<div className="comment">
<p className="comment-header">{this.props.author}</p>
<p className="comment-body">{this.props.body}</p>
<div className="comment-footer">
<a href="#" onClick={this._handleDelete.bind(this)} className="comment-footer-delete">
Delete comment
</a>
</div>
</div>
);
}
_handleDelete(event) {
event.preventDefault();
if (confirm('Are you sure?')) {
this.props.onDelete(this.props.comment);
}
}
}
// this now allows us to define a <Comment/> react component in our JSX and
// - pass props to it
// - change code according to state (we'll need a constructor to handle state defaults)
class CommentBox extends React.Component {
constructor() {
super(); // must be first line called in a constructor
this.state = {
showComments: false,
comments: [ ] // initialize this to something empty that we'll populate later
};
}
render () {
const comments = this._getComments();
let commentNodes;
let buttonText = 'Show comments';
if (this.state.showComments) {
buttonText = 'Hide comments';
commentNodes = <div className="comment-list">{comments}</div>;
}
return(
<button onClick={this._handleClick.bind(this)}>{buttonText}</button>
<div className="comment-box">
<CommentForm addComment={this._addComment.bind(this)} />
<h3>Comments</h3>
<h4 className="comment-count">{this._getCommentsTitle(comments.length)}</h4>
{commentNodes}
</div>
);
}
// this gets called BEFORE component is rendered
componentWillMount() {
_fetchComments();
}
// this is called just AFTER component is rendered
componentDidMount() {
// poll the server every 5s...
// To avoid memory leaks, capture the timer and unmount it later
this._time = setInterval(
() => this._fetchComments(),
5000
);
}
// this is called when component is ABOUT TO BE REMOVED
componentWillUnmount() {
clearInterval(this._timer);
}
// pass this to the <Comment /> to communicate results
_deleteComment(comment) {
someDeleteMethod(comment.id) // don't set up a callback or wait for confirmation of deletion
// remove the passed in comment, client-side
const comments = [...this.state.comments]; // use ...-operator and [] to clone array
const commentIndex = comments.indexOf(comment);
comments.splice(commentIndex, 1);
this.setSate({ comments }); // for a UI update
}
// calling this function in render() will create an infinite loop, since
// render is always called after this.setState()
_fetchComments() {
someGetRequest(comments => this.setState({ comments }) ) // could use jQuery.ajax or whatever to fetch the comments
}
_addComment(author, body) { // this is passed to and called from the <CommentForm />
const comment = {author, body};
makePostRequest(newcomment => {
// the successful post request should return an object with a unique key
// use concat instead of push to avoid mutating the comments array; it helps react stay fast
this.setState({ comments: this.state.comments.concat([comment]) });
})
}
_handleClick() {
// calling setState forces the component to re-render...
this.setState({
showComments: !this.state.showComments // this will modify ONLY the showComments property
});
}
_getCommentsTitle (commentCount) {
if (commentCount === 0) {
return 'No comments yet';
} else if (commentCount === 1) {
return '1 comment';
} else {
return `${commentCount} comments`;
}
}
_getComments () { // underscored functions distinguish custom vs. react methods
return this.state.comments.map((comment) => {
// good practice to pass in a unique value as a components key -- can help improve performance
return(
<Comment
author={comment.author}
body={comment.body}
key={comment.id}
onDelete={this._deleteComment.bind(this)} /> // bind the scope of _deleteComment to this environment
);
});
}
}
// create a CommentForm component that demonstates _refs_:
// Refs allow us to reference DOM elements in our code, after the component has been rendered.
// NOTE: react runs the ref call backs on render()
class CommentForm extends React.Component {
render() {
return (
<form className="comment-form" onSubmit={this._handleSubmit.bind(this)}>
<label>Join the discussion</label>
<div className="comment-form-fields">
<input placeholder="Name:" ref={(input) => this._author = input}/>
<textarea placeholder="Comment:" ref={(textarea) => this._body = textarea}>
</textarea>
</div>
<div className="comment-form-actions">
<button type="submit">
Post comment
</button>
</div>
</form>
);
}
_handleSubmit(event) {
event.preventDefault(); // prevent page from reloading, when form is submitted.
// grab information populated from refs in JSX
let author = this._author;
let body = this._body;
this.props.addComment(author.value, body.value); // this method has been passed as an argument to <CommentForm/>
}
}
@stevenpollack
Copy link
Author

Check out the list of React synthetic events (e.g. onSubmit or onClick)...

@stevenpollack
Copy link
Author

There's also component lifecycle methods that dictate when certain remote calls should be made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment