It's hard to maintain a state. Harder to maintain a UI. And near impossible to keep a user happy.
No matter how well you plan, disintegration always happens.
React is a well-known web UI library with a long history of state implementation. Components shape the application. But it's a state's job to keep it inside the sanity borders.
That is unless it drives the developer insane before achieving this goal!
Different seasons, different states.
We know how the seasons pass. In spring, flowers bloom. In summer, sweats run down the armpits. With autumn, leaves leave the branches. And who will forget the snowy days of winter? (Hint: People who live after global warming makes its debut!)
Nature can resemble a big React Component
. A part of life on Earth. But it goes through various phases. We know each as a season.
Different parts of a front-end application can be in different phases too. They are the results of changes in what we call a state
.
An introduction to our study.
Imagine a man with a bad temper. He swears in hot summers. And beats people when it gets cold enough. No one can predict him.
You might say that he is affected by the weather.
For example, he is a kind man during springs and even sings poems when it rains. Winters are cold and make him impatient. That is why he will prefer to get his deals done by action rather than talks.
Such a man lacks good state management, making him unable to control his behavior. A similar issue can exist in a front-end application too. That’s when it gives inappropriate responses or gives none at all.
Then, let's have a quick review of what a state is in a React Component.
React Components State.
From the React documentation:
State is similar to props, but it is private and fully controlled by the component.
Props
themselves are Objects
. Their keys
are the name of the attributes
we pass to the component through JSX
. And their values
are that of the corresponding attributes.
While a state can be an attribute or a collection of them, it can be an Object
, a String
, or any other JavaScript Type
. But what is the main difference between a state and a prop
?
Answer: The component
receives props
from its parent, while it creates and controls
its own state
.
What is the actual problem?
At this point we can ask a question: What makes a great application?
Perhaps dedication to its end. The desire to deliver satisfaction. A sense of being useful. What we may call confidence.
A bad application isn't pushed by these things. It already feels satisfied and confident. It is directed by more solid things. It is affected by data flows. By a powerful inertia.
It's uncareful about user expectations. And it's rarely suited for different user stories. It only works if everything is ok. Which is seldom the case in a real world application.
Can we make a state using a simple variable?
Let's say that we've a component called Nature
. This component has a main state variable: season
. First let's declare it just using a simple JavaScript variable
:
function Nature(props) {
let season = 'spring';
return(
<p>Currently it is {season}</p>
)
}
The line const season = 'spring';
is where we declared our state variable. The output HTML looks like this:
<p>Currently it is spring</p>
Let's add a button to our function's return. We pass its onClick
event a callback which will try to change the variable season
's value to 'summer':
function Nature(props) {
let season = 'spring';
const changeSeason = () => {
season = 'summer'
}
return(
<div>
<p>Currently it is {season}</p>
<button onClick={changeSeason}>Click to change season!</button>
</div>
)
}
It will output the previous result with an added button. Yet if you click on the button, it won't change the season's name in the output.
The variable's value changes to summer but the onClick
will never result in a change in the output.
Why?
To answer that we should learn about React Hooks Flow
. This is where things get interesting.
Stories of a component's madness.
Events. A sequence of them shapes what we may know as a storyline. For example, you may have just graduated from high school. This is an important event for you that also describes your level of education.
Our memories and personalities are made of events. Our resumes are fully contented by them. And to get our life move on, we expect future events too.
A Component's lifecycle is full of events. We're going to have a look at an example.
Assume a submit button which is used to register user information. The button is disabled until the user fills the required fields correctly. The form component uses a state variable to enable the button.
It works just fine. The user fills in the form. The gray button turns blue and the user clicks on it. In the background the API request for registering is sent. But the user is not informed. They don't see any loading element or message.
They think it hasn't worked and they click on it again. And guess what! Another request is sent in the background. The first request is successful to register the user.
The component is designed to redirect the user after success. But...
The response to the second request comes in a hurry. Acknowledging the component that the user's email is a duplicate. Now another state variable holding the email duplication error, turns true.
The component gives the user an error informing that the email is a duplication and the redirect just doesn't work because the error state is not false. The component has been instructed not to redirect if the error state is not clear. Which is irrelevant. And is a bad state management.
We're faced with several events in this example. One of them happens when the form is filled. Another occurs when the user clicks on the button. The last event is when the response has arrived.
We as users and developers can understand these events. But a UI library such as React is not as smart as a human. It has to be instructed what the events are, and it has to register them beforehand.
That's what we call Components Hook Flow
.
React Components Hook Flow
Hooks were added to React from version 16.8. They were a major update at the time, as they empowered the stateless function components with abilities that were only available in Class Components.
It was a good thing to have the ability to run effects and change states in a functional component. But a Class Component also provided the developers with Lifecycle Methods, eg. componentDidMount or shouldComponentUpdate.
The functional components and hooks don't provide us with Lifecycle Methods. Instead different hooks in a functional component are run in a specific order so that the developer can implement the same Lifecycle Logics by considering this order.
The Hooks Flow is made of 3 main stages: Mount
, Update
and Unmount
.
When a component mounts for the first time, its initial values are set. These include the useState
and useReducer
initializer functions. Then it will continue the rest of the codes you have added in your functional component, up until it reaches the returned value.
Then before rendering the returned JSX, it will run your layoutEffects
which you create using the useLayoutEffect
hook. Then the browser paints the screen to reflect the React Virtual DOM. Then the effects
which you have registered using useEffect
are called.
It's a straightforward flow for mounting the component. But then the component will need to update. This can happen due to two reasons: either there's a change in props or a state has been updated.
The updating stage has its own steps: It will run your functional component and update the DOM based on the new changes resulting from the updated state.
In the next step, it's going to clear the previous layoutEffects, afterward, it will run the layoutEffects. The browser repaints the screen to reflect the changes.
And finally, before running the effects, react is going to clean the previous effects.
The updating stage has a similar order of steps to the mounting one. It only differs in the details. Like, the mounting stage uses initial state values, and the updating stage uses the new ones. The mounting stage runs the effects but the updating one first will try to clear the effects caused by previous updating or mounting stages of the component.
The third stage in this flow as we mentioned is the unmounting stage. In this stage the whole component will be cleared from the screen. So nothing is going to happen, except that React will try to clear any remaining layoutEffects and effects.
Now that we know the React Hooks Flow, we can realize why storing our Component's state inside a simple variable won't cause a change in the DOM.
Because React won't update the DOM, unless it is sure that something has changed.
The way React listens to the state changes is like how we can add eventListeners in JS. For example, assume a text input element. We can add listeners for its value change
, or when the input is blurred
.
React adds listeners to the state variables' changes. And when we call the stateSetter functions, this event is fired, and then React knows how to update the DOM.
Then, let's rewrite our previous code to make it work.
The right way to declare a state
React provides us with the useState
hook to bring states to functional components. To initialize a state you need to call the hook and pass it the state's initial value
.
The hook will return an array of two elements. The first element in the array is the state's value
and the second element is the state-setter function
.
We'll use this function to add the season state to our component. We'll also rewrite our changeSeason
function to use the state-setter
function returned by useState
.
function Nature(props) {
let [season, setSeason] = useState('spring');
const changeSeason = () => {
setSeason('summer')
}
return(
<div>
<p>Currently it is {season}</p>
<button onClick={changeSeason}>Click to change season!</button>
</div>
)
}
Now after rendering this component if we try to click on the button we'll see the season name in the paragraph changes to summer
.
In the above code, the variable season
refers to the first element returned by useState which holds the latest state value. And setSeason is the method we can use to update the state's value.
We learned how we can bring states to a functional component in React. And we've also learned how states work in a more fundamental way.
Knowing the right way to declare a state, a great question pops up: Is there also a right way to use states in React?
Life is a journey (So is React development)
No one is like anyone else. No one shares the exact same interests as anyone else. We're all unique through our preferences. And this uniqueness also affects the way we live our lives.
Different React projects also have their own differences. They differ in how they manage their states, their effects, their components trees, or even their folder structures.
No one state that you should structure a React project in a specific way. You have to understand the underlying mindset which React uses for managing its re-renders, props, states, effects and etc.
Throughout this article, we learned about the mindset behind how React implements states. I hope it can help you to better understand what they are and why they matter.
This is already a long article and I'll end it here as I don't want to bore you with extra information. But let me add one more tip. Anytime you were in doubt whether you should use a state or not, try to answer this question: Should changes in this variable cause a re-render?
Credits for cover image: M. Schuppich/Shutterstock.
I have no content creation experience in English Twitter yet. But I'd like to start tweeting about different subjects of programming in my Twitter account. I'd really be thankful of your support if you start following my twitter account:)
Top comments (8)
Great article! I really enjoyed it.
Thanks. I'm glad you liked it!
Good read.
Thanks for the feedback Andrew!
I look forward to reading your next articles, good luck.
Thanks for the positive energy!
This is a very good explanation of the material!
Thanks. I'm glad it was helpful!