In this blog post, we will discuss how to interact with the state of your React components. The state is a crucial part of managing component data and handling user interactions.
Setting the Default State
To set the default state of a component, you need to initialize this.state
in the constructor. For example, let’s consider a BlogPostExcerpt
component with a clicked
state:
class BlogPostExcerpt extends Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
);
}
}
Accessing the State
To access the state, you can simply reference this.state.clicked
within your component:
class BlogPostExcerpt extends Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
<p>Clicked: {this.state.clicked}</p>
</div>
);
}
}
Mutating the State
It’s important to note that you should never mutate the state directly by assigning values to this.state.clicked
. Instead, always use the setState()
method to update the state. Here’s an example:
this.setState({ clicked: true });
The setState()
method takes an object as an argument, which can contain a subset or a superset of the state properties. Only the properties you pass will be mutated, while the others will remain unchanged.
Why You Should Always Use setState()
Using setState()
is important because it allows React to track state changes. When you call setState()
, React knows that the state has changed, and it triggers a series of events that eventually leads to the component being re-rendered, along with any necessary DOM updates.
Unidirectional Data Flow
In React, the state is always owned by a single component. Any data affected by this state can only affect components below it, i.e., its children. Modifying the state of a component will never impact its parent, siblings, or any other component in the application. This is known as “Unidirectional Data Flow.”
To share state between components, you need to move the state up to a common ancestor. While the closest ancestor is often the best place to manage the state, it’s not a strict rule. The state can be passed down to the components that require the value via props.
class Converter extends React.Component {
constructor(props) {
super(props);
this.state = { currency: '€' };
}
render() {
return (
<div>
<Display currency={this.state.currency} />
<CurrencySwitcher currency={this.state.currency} />
</div>
);
}
}
Moving the State Up in the Tree
To share state between components, you can move the state up the component tree. As per the Unidirectional Data Flow rule, if two components need to share state, the state needs to be moved up to a common ancestor. This allows multiple components to access and update the shared state.
In the following example, the Converter
component manages the currency
state, which is then passed down to its child components via props.
class Converter extends React.Component {
constructor(props) {
super(props);
this.state = { currency: '€' };
}
handleChangeCurrency = () => {
this.setState((prevState) => ({
currency: prevState.currency === '€' ? '$' : '€'
}));
}
render() {
return (
<div>
<Display currency={this.state.currency} />
<CurrencySwitcher
currency={this.state.currency}
handleChangeCurrency={this.handleChangeCurrency}
/>
</div>
);
}
}
const CurrencySwitcher = (props) => {
return (
<button onClick={props.handleChangeCurrency}>
Current currency is {props.currency}. Change it!
</button>
);
}
const Display = (props) => {
return <p> Current currency is {props.currency}.</p>
}
In the Converter
component, we define a handleChangeCurrency
function that updates the currency state based on the current currency value. This function is then passed down to the CurrencySwitcher
component as a prop, allowing it to mutate the state when triggered.
By following this pattern, you can efficiently manage and update the state of your React components, ensuring a smooth and maintainable application.