I’ve been using ReactJS with Redux at work recently, and I have quite a few questions about how it all fits together. I figured I’d start small, with just some React questions and see how it goes.
This “props vs. state” question is pretty common for new React devs - they look so similar, but are used differently. So what’s going on there?
Props
What does “props” even mean?
To get the jargon out of the way, “props” is short for “properties” so nothing particularly fancy there.
Well, all right then. What makes props special?
props are passed into the component
Here’s an example (code from the React Guide):
1
2
3
4
5
6
7
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
const element = <Welcome name="Sara" />;
You can play with this on CodePen.
The line <Welcome name="Sara" /> creates a property name with value "Sara".
That sounds kinda like a function call...
Yep, the property is passed to the component, similar to how an argument is passed to a function. In fact, we could even rewrite the component to be simpler:
1
2
3
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
Now the “props as arguments” comparison is even clearer.
OK, so props “come from above.”
Often, but not always. A component can also have default props, so if a prop isn’t passed through it can still be set.
We can make the name property optional by adding defaultProps to the Welcome class:
1
2
3
4
5
6
7
8
9
class Welcome extends React.Component {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
Welcome.defaultProps = {
name: "world",
};
If Welcome is called without a name it will simply render <h1> Hello world</h1>.
So props can come from the parent, or can be set by the component itself.
props should not change
What?! I’ve totally done that!
You used to be able to change props with setProps and replaceProps but these have been deprecated. During a component’s life cycle props should not change (consider them immutable).
Fine, I won’t change props any more.
Since props are passed in, and they cannot change, you can think of any React component that only uses props (and not state) as “pure,” that is, it will always render the same output given the same input. This makes them really easy to test - win!
State
Like props, state holds information about the component. However, the kind of information and how it is handled is different.
By default, a component has no state. The Welcome component from above is stateless:
1
2
3
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
So when would you use state?
When a component needs to keep track of information between renderings the component itself can create, update, and use state.
We’ll be working with a fairly simple component to see state working in action. We’ve got a button that keeps track of how many times you’ve clicked it.
Yawn...
I know, but here’s the code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Button extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
updateCount() {
this.setState((prevState, props) => {
return { count: prevState.count + 1 }
});
}
render() {
return (<button
onClick={() => this.updateCount()}
>
Clicked {this.state.count} times
</button>);
}
}
You can play with this code on CodePen.
Gah! There’s so much there! What’s going on?
So now we’re working with state things are a bit more complicated. But we’ll break it down to make it more understandable.
Our first real difference between props and state is that...
state is created in the component
Let’s look at the constructor method:
1
2
3
4
5
6
constructor() {
super();
this.state = {
count: 0,
};
}
This is where state gets it’s initial data. The inital data can be hard coded (as above), but it can also come from props.
Well that’s just confusing.
It is, I know. But it makes sense - you can’t change props, but it’s pretty reasonable to want to do stuff to the data that a component receives. That’s where state comes in.
Moving on brings us to our second difference...
state is changeable
Here’s updateCount again:
1
2
3
4
5
updateCount() {
this.setState((prevState, props) => {
return { count: prevState.count + 1 }
});
}
We change the state to keep track of the total number of clicks. The important bit is setState. First off, notice that setState takes a function, that’s becuase setState can run asynchronously. It needs to take a callback function rather than updating the state directly. You can see we have access to prevState within the callback, this will contain the previous state, even if the state has already been updated somewhere else. Pretty slick, huh?
But React goes one step better, setState updates the state object and re-renders the component automagically.
Boom!
Yeah, this is pretty great of React to do, no need for us to explicitly re-render or worry about anything. React will take care of it all!
setState warning one!
It is tempting to write this.state.count = this.state.count + 1. Do not do this! React cannot listen to the state getting updated in this way, so your component will not re-render. Always use setState.
setState warning two!
It might also be tempting to write something like this:
1
2
3
4
// DO NOT USE
this.setState({
count: this.state.count + 1
});
Although this might look reasonable, doesn’t throw errors, and you might find examples that use this syntax online, it is wrong. This does not take into account the asychronous nature that setState can use and might cause errors with out of sync state data.
Program as usual
And finally, render
1
2
3
4
5
6
7
render() {
return (<button
onClick={() => this.updateCount()}
>
Clicked {this.state.count} times
</button>);
}
onClick={() => this.updateCount()} means that when the button is clicked the updateCount method will be called. We need to use ES6’s arrow function so updateCount will have access to this instance’s state.
The text rendered in the button is Clicked {this.state.count} times, which will use whatever this.state.count is at the time of rendering.
Phew! That was a lot! Can I have it one more time?
Sure thing, let’s look at the whole flow:
The component is initialised and
state.countis set to 01 2 3
this.state = { count: 0, };The component renders, with “Clicked 0 times” as the button text
1
Clicked {this.state.count} timesThe user clicks the button
click!
updateCountis called, bound to this instance of the component1
onClick={() => this.updateCount()}updateCountcallssetStatewith a call back to increase the counter from the previous state’s counter value1 2 3
this.setState((prevState, props) => { return { count: prevState.count + 1 } });setStatetriggers a call torenderReact magic!
The component renders, with “Clicked 1 times” as the button text
1
Clicked {this.state.count} times
Review
While props and state both hold information relating to the component, they are used differently and should be kept separate.
props contains information set by the parent component (although defaults can be set) and should not be changed.
state contains “private” information for the component to initialise, change, and use on it’s own.
... props are a way of passing data from parent to child. ... State is reserved only for interactivity, that is, data that changes over time.
– Facebook's React Guide.
Resources
- Amazing guide on state vs. props
- Stackoverflow question
- Docs on Components and Props
- Docs on State and Lifecycle