'Why side effect is not ok in React
I was learning React and localStorage. Then, I came across an article saying that localStorage is best to be used via useEffect() due to side-effects. So, this code is bad:
import React from 'react';
const App = () => {
const [value, setValue] = React.useState('');
const onChange = event => {
localStorage.setItem('myValueInLocalStorage', event.target.value);
setValue(event.target.value);
};
return (
<div>
<h1>Hello React with Local Storage!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
export default App;
But this code is right:
import React from 'react';
const App = () => {
const [value, setValue] = React.useState('');
React.useEffect(() => {
localStorage.setItem('myValueInLocalStorage', value);
}, [value]);
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React with Local Storage!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
export default App;
One question WHY? Why is wrong with the first code and what re the benefits of the second code with useEffect()
Solution 1:[1]
Both cases of your code are fine. The reason the first case doesn't need to be wrapped in useEffect
is because it's already in an event handler, and it will be called only once per change. Likewise, the reason we use useEffect
in the second case is so that React can ensure it only calls the effect once.
Why bother? React may retry rendering your component when it likes, and it expects the same JSX for the same state/props regardless of how many times it renders--i.e., your component should be "pure". This enables lots of performance enhancements like Suspense, but you don't need to concern yourself with that as long as you don't run side-effects directly in the render function.
Solution 2:[2]
As a programmer, It is easier to manage state, when you are not responsible for synchronising it. As soon as you miss synchronising any part of state, whole state becomes corrupt. useEffect gives you functionality about how should your code behave when any particular part changes. Seems more reactive.
const onChange = event => {
localStorage.setItem('myValueInLocalStorage', event.target.value);
setValue(event.target.value);
};
In above snippet, you are synchronising two states, React state and localstorage as well.
const [value, setValue] = React.useState('');
React.useEffect(() => {
localStorage.setItem('myValueInLocalStorage', value);
}, [value]);
const onChange = event => setValue(event.target.value);
In above snippet, you are synchronising only one state, React is doing the rest of synchronising. This does not seem very convincing when you are dealing with 2, 3 states but it is a lifesaver when state starts to grow.
Solution 3:[3]
The benefit of using useEffect is that you can clean up when unnecessary.
React.useEffect(() => {
localStorage.setItem('myValueInLocalStorage', value);
return (() => { // clean up
// remove when unnecessary
if (true) { // your condition
localStorage.removeItem('myValueInLocalStorage');
// Or even set empty value
}
});
}, [value]);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | joshwilsonvu |
Solution 2 | gautamits |
Solution 3 | Bhojendra Rauniyar |