
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):
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:
function Welcome(props) {
return <h1>Hello {props.name}</h1>;
}
Now the “props as arguments” comparison are 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:
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:
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 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:
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:
constructor() {
super();
this.state = {
count: 0,
};
}
This is where state
gets its initial data. The initial data can be hardcoded (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 the state comes in.
Moving on brings us to our second difference…
state
is changeable
Here’s updateCount
again:
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 because 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 to 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:
// 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 asynchronous nature that setState
can use and might cause errors with out of sync state data.
Program as usual
And finally, render
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 0
this.state = {
count: 0,
};
2. The component renders, with “Clicked 0 times” as the button text
Clicked {this.state.count} times
3. The user clicks the button.
click!
4. updateCount
is called, bound to this instance of the component
onClick={() => this.updateCount()}
5. updateCount
calls setState
with a call back to increase the counter from the previous state’s counter value
this.setState((prevState, props) => {
return { count: prevState.count + 1 }
});
6. setState
triggers a call to render
React magic!
7. The component renders, with “Clicked 1 times” as the button text
Clicked {this.state.count} times
Should this Component have state?
“state” is optional. Since state increases complexity and reduces predictability, a Component without “state” is preferable. Even though you clearly can’t do without “state” in an interactive app, you should avoid having too many Stateful Components.
Component types
- Stateless Component — Only props, no state. There’s not much going on besides the
render()
function and all their logic revolves around the props they receive. This makes them very easy to follow (and test for that matter). We sometimes call these dumb-as-f*ck Components (which turns out to be the only way to misuse the F-word in the English language). - Stateful Component — Both props and state. We also call these state managers. They are in charge of client-server communication (XHR, web sockets, etc.), processing data and responding to user events. These sort of logistics should be encapsulated in a moderate number of Stateful Components, while all visualization and formatting logic should move downstream into as many Stateless Components as possible.
Review
While props
and state
both hold information relating to the component, they are used differently and should be kept separate.
props
contains the 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 its 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.