Let's learn about recoil
While recently working on a project, I fell into the loophole of passing props from parent components to my child components and passing them way down the hierarchy. Is it ugly? Yes…. Is it optimal? Really not….. For a small-scale application, it may not give any performance issues but for a complex application that would severely slow our application. Passing down the props also introduces the demon of re-rendering. Let’s take a simple example
function App() {
const [count, setCount] = useState(0)
return (
<>
<Increase setCount={setCount} />
<Decrease setCount={setCount} />
<Value count={count} setCount={setCount} />
</>
)
}
function Increase({setCount}){
return <button onClick={() => setCount((c) => c + 1)}>
Increase
</button>
}
function Decrease({setCount}){
return <button onClick={() => setCount((c) => c - 1)}>
Decrease
</button>
}
function Value({ count }) {
return <p>Count: {count}</p>;
}
Over here we have introduced a simple Parent Component (App) that has three child components Increase, Decrease and Value. The parent passes down the setter function to Increase and Decrease and state variable to value. Now lets run the application and check how many re-renders takes place. Can you guess the re-renders? Will it only rthe Value Component Re-render or would All the Child Components Re-render?
You can make use of Chrome Dev Tools Extension to see the changes yourself. Once installed click on Inspect Element → Components → General → Select “Highlights Updates When Components Re-render”
Alright…. so what happened here? The Green Highlight in Chrome Dev Tools means a component has been re-rendered and in our case, three child components have re-rendered (Increase, Decrease and Value). The body of Increase and Decrease Component do not change throughout the process, they shouldn’t re-render, right? Shouldn’t only the Value Component re-render? In React, whenever the state updates, the component that holds the state (in this case, App) will re-render and therefore its children would re-render as well in the process. How do we make it stop and ensure only the Value Component re-render as its body does change during the execution.
Introduction Recoil……
You can check out the beautiful explanation of how Recoil works under the hood. Explained by one of its creators Dave McCabe.
import { RecoilRoot, atom, useRecoilValue, useSetRecoilState } from 'recoil';
const count = atom({
key: 'countState', // unique ID (with respect to other atoms/selectors)
default: 0, // default value (aka initial value)
});
function App() {
return (
<>
<RecoilRoot>
<Increase />
<Decrease />
<Value />
</RecoilRoot>
</>
)
}
function Decrease() {
const setCount = useSetRecoilState(count);
return <button onClick={() => setCount(count => count - 1)}>Decrease</button>;
}
function Increase() {
const setCount = useSetRecoilState(count);
return <button onClick={() => setCount(count => count + 1)}>Increase</button>;
}
function Value() {
const countValue = useRecoilValue(count);
return <p> Count: {countValue}</p>;
}
Alright! Let’s take a deep breath here and understand things slowly.
Recoil is a global state management tool that allows us to share and use state variables across our application and free us from the shackles of prop drilling. Notice how we have eliminated sharing down props to our child components and are now managing the state variables globally.
We have wrapped our components inside RecoilRoot as it allows any component within its tree to access and modify Recoil state atoms.
Also, we have defined an atom at the top, which are the units of state or variables (or better say it’s a useState variable that is available globally, rather than a component) that components can subscribe to.
useSetRecoilState helps us retrieve the setter function for that atom which is used in Increase and Decrease component
useRecoilValue helps us to subscribe to a state variable in a particular component, in our case its Value
Let’s check the re-render now….
Voilaaaaa we have managed to isolate the re-render to Value component and now our application is running smoothly. Say goodbye to the hassle of prop drilling or lifting up the state to the parent component so that the other children can have the state. Recoil saves the day by helping us manage our state globally.
In the next part, I will talk in detail about Selectors in Recoil.
Note:
Some may point out that I could have used Context API but it primarily addresses the issue of prop drilling by allowing you to share state across your component tree without needing to pass props through every level.
It doesn’t optimise renders in your application, which becomes important if/when your application becomes bigger
Subscribe to my newsletter
Read articles from Chaitanya Shetty directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by