This read is highly recommended if you are just beginning to work with React. Many common problems arise to all developers working with this new paradigm of UI development. I've also included different ways of writing code syntactically in the examples in React.
setState()
Use this.setState()
to change state. It triggers a re-render. this.state.someValue = value
does not trigger a re-render.
Why, you ask? React is fast because it re-renders only the things that need to be re-rendered, rather than everything on the page. By default, React components will re-render when the props passed to the component change, or when the state changes. Props shouldn't be set by the component receiving them, but state can, so to track these changes, React requires developers to set the state using this function. An analogy to regular programming: props are like parameters to a function and state is the global variables available to the function.
In fact, a React Component is a pure function of the state and props, meaning given the same state and props, the component should be the exact same.
setState()
is asynchrounoussetState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. - Official docs
For example, MyComponent takes in prop increment
, which defines how much is incremented when the button is pressed.
class MyComponent extends React.Component {
state = {
number: 0
}
// BAD
increment = e => {
this.setState({
number: this.state.number + this.props.increment
})
}
// GOOD
increment = e => {
this.setState((prevState, props) => ({
number: prevState.number + props.increment
}))
}
render(){
return(
<button onClick={ this.increment }>{ this.state.number }</button>
)
}
}
Say we used the bad version and this.props.increment
was 1 and this.state.number
was 0. If we clicked the button, the state would be 1
because the state was 0
previously. If we quickly press it again, we would think that the number would be changed to 2 because the button was pressed twice and the "previous" state was 1. However, this may not happen because of the asyncrhonity of setState
. The second call may still see that the state is still 0. Thus, use the second version whenever your next state is dependent on the previous state.
.bind
vs Arrow FunctionsArrow functions auto bind :) One of the first things that may happen is using the .bind
operator on class functions that handle events from HTML elements such as buttons or input forms. This is because the compiler, Babel, will return an error message yelling at you to bind
this certain function. You may end up with something like this:
class MyComponent extends React.Component {
constructor(props){
super(props)
}
handleClick(event) {
console.log("button was clicked")
}
render() {
return(
<button onClick={this.handleClick.bind()}>Click Me!</button>
)
}
}
This works and compiles! However, the function handleClick()
is binded to the class everytime the state changes and the Component is re-rendered, which happens quite a bit. To fix it you may do inside the constructor:
this.handleClick = this.handleClick.bind(this)
However, why not remove that line and instead use Arrow functions.
class MyComponent extends React.Component {
handleClick = (event) => {
console.log("button was clicked")
}
render() {
return(
<button onClick={ this.handleClick }>Click Me!</button>
)
}
}
As a rule of thumb, if a certain prop that you pass into a Component does not change inside that Component, directly access props and do not set a state attribute equal to that prop.
// BAD
class TextBoxComponent extends React.Component{
constructor(props){
this.state = {
text: this.props.text
}
}
render() {
return (
<div>
<p>{ this.state.text }</p>
</div>
)
}
}
// GOOD
class TextBoxComponent extends React.Component{
render(){
return (
<div>
<p>{ this.props.text }</p>
</div>
)
}
}