In the previous lesson, we used the concept of state from React to store and update the current image index using the useState
hook:
Have you ever thought, why do we even need a state? Can't we use a local variable inside the component and update it directly, as shown below:
Let's investigate this by replacing state with a local variable in the Carousel
component. For the sake of simplicity, here we have shown a minimal version of the Carousel
component to test this out:
Now, check if the Carousel
component works as before. You can see that the currentIndex
variable gets updated and logged into the console, but the UI remains the same. This happens because the changes to the local variables don't trigger a re-render to update the component with the new data.
Even if our component was somehow re-rendered, changes to the local variables won't persist between re-renders. This is because every time React wants to re-render the Carousel
component, it invokes the Carousel
function again. As you might already know, calling a function will initialize all the local variables defined within it afresh. Therefore, every time React re-render the Carousel
component, the local variable currentIndex
will get reset to 0
.
React state overcomes the above limitations of the local variables using:
- A state variable to retain the data between renders, and
- A state setter function to update the variable and trigger React to render the component again.
The useState
hook acts as an interface to access the React state.
When a component is invoked for the first time, the useState
hook creates a state and assigns the initial value passed to it. React will then build all the necessary DOM nodes corresponding to the component using the initial value of the state variable. In our case, the currentIndex
state variable is initialized with the value 0
, and the initial JSX is returned, with the first image URL set as the value for the src
attribute.
Eventually, when we invoke the state setter function setCurrentIndex
, we are telling React to update the state variable currentIndex
and trigger a re-render. React then generates a new description of the UI with the latest value of the state variable by invoking the component function again.
It is essential to understand that React doesn't alter the entire component's browser DOM during state updates. Instead, it compares the React elements generated after the state update with the previous one, updating only the parts of the DOM that require changes.
For the Carousel
component, these changes involve updating the src
prop of the img
tag and altering the background colors of the appropriate span
elements. React executes these changes using primitive JavaScript methods under the hood. The following is a simplified overview of the DOM updates that occur behind the scenes for our Carousel
component.
The above process is summarized in the following diagram:
Avoid creating unnecessary states
Despite the discussed limitations of local variables, there are scenarios where we prefer them over state variables.
Consider the UserForm
component below, which contains a state variable birthDate
and renders the age
of the user along with birthDate
. In this scenario, it is unnecessary to create a separate state variable for holding the value age
. Since age
can always be deduced from birthDate
, we can use a local variable to calculate its value during every re-render. Also, there will never be a case where we want to change age
alone and trigger a re-render. So setAge
doesn't make any sense while we have setBirthDate
already in place.
As illustrated in the above example, whenever we can derive something from existing props or states, we should calculate it during rendering itself instead of creating a new state. This way, we can avoid unnecessary re-rendering of components due to state updates, making the code faster and simpler. Also, minimizing the number of degrees of freedom of the component makes them more reliable.
The changes to the Carousel
components were just for learning purposes. So, let's clean up those changes by running the following command: