SAM
SAM

Reputation: 1121

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()

Upvotes: 13

Views: 2530

Answers (4)

Navpreet
Navpreet

Reputation: 101

Both options are fine. In your case the localstorage is not a side effect because it's used in an event listener. \

If the requirement is to update localstorage whenever value changes, no matter the source (maybe input field or maybe something else) then I would go with option 2
else with option 1

Found this short youtube video related to react side effects which I think would be helpful here.

Upvotes: 0

gautamits
gautamits

Reputation: 1292

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.

Upvotes: -1

joshwilsonvu
joshwilsonvu

Reputation: 2689

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.

Upvotes: 5

Bhojendra Rauniyar
Bhojendra Rauniyar

Reputation: 85545

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]);

Upvotes: -2

Related Questions