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.count
is 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} times
The user clicks the button
click!
updateCount
is called, bound to this instance of the component1
onClick={() => this.updateCount()}
updateCount
callssetState
with 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 } });
setState
triggers a call torender
React 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