Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React state not giving correct value on useEffect()

I'm trying to build a factorization algorithm using react. I would like to add results to LocalStorage based on results from factorization. However, LocalStorage sets previous results not current ones.

I think this is happening because useEffect runs on every new [number] (=user input) and not based on [results]. However, I need useEffect to run on new user input submition because that's when factorization has to be triggered.

How could I make localStorage set correct results after that factorization has completed (on the finally block if possible) ?

const [results, setResults] = useState({
  default: [],
  detailed: [],
  isPrime: '',
});

const { number } = props

useEffect(() => {
  handleWorker(number);
  //accessing results here will give previous results
}, [number]);

const handleWorker = number => {
  try {
    const worker = new Worker(new URL('facto.js', import.meta.url));
    worker.postMessage({ number, algorithm });
    worker.onmessage = ({ data: { facto } }) => {
      setResults({ ...facto });
      //the worker 'streams live' results and updates them on setResults
    };
  } catch(error) {
    console.log(error.message)
  } finally {
    localStorage.setItem(number, results)
    //here, results is not current state but previous one
  }
};

Please note that everything else works fine

Thanks

like image 303
Gibberish Avatar asked Dec 23 '21 11:12

Gibberish


People also ask

Why useState is not updating immediately?

The answer: They're just queues setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.

Does React useState hook update immediately?

React do not update immediately, although it seems immediate at first glance.

Can useEffect return a value?

you can return your value and put in state and use it outside useEffect. if your value need preState, remember to pass an arrow function in setState and pass preState as a parameter.

What is the best way to update state in react form?

React hooks heavily depends on closure, besides useEffect hook, if your new value based on old value defined by useState hook, than make sure you pass a function of form: setNewState ( (currentValue) => newValue) to set new state, React will assure you always gets the freshest state value when updating. Show activity on this post.

Does modifying state cause react rendering problems?

If you modify state directly (mutate it) then it can cause problems with re-rendering, but not always. It can cause problems if you render any pure components in React. Pure components will assume that the props passed to them won’t be mutated. They make this assumption so that React can make object comparisons more quickly.

How to update the state of a useeffect with useeffects dependencies?

To do something in the state's current value in the useEffect 's return function where the useEffects dependencies are am empty array [], you could use useReducer. This way you can avoid the stale closure issue and update the state from the useReducer 's dispatch function.

Why does react make assumptions about state?

They make this assumption so that React can make object comparisons more quickly. Although you can get away with it often, as a rule it is best not to modify state directly, and instead update the state to a brand new object. There is a lot more on this subject in this blog post: Why Not To Modify React State Directly


2 Answers

You are getting the previous value because localStorage.setItem is executed before setResults updates the state. Yo can do some refactor to make it work:

const [results, setResults] = useState({
  default: [],
  detailed: [],
  isPrime: '',
});

const { number } = props;
const workerRef = useRef(new Worker(new URL('facto.js', import.meta.url)));
const worker = workerRef.current;

useEffect(()=> {
    //-> create the listener just once
    worker.onmessage = ({ data: { facto } }) => {
      setResults({ ...facto }); 
    };
}, []);

useEffect(() => {
   //-> send message if number changed
   worker.postMessage({ number, algorithm });
}, [number]);

useEffect(() => {
   //-> update localStorage if results changed
   localStorage.setItem(number, results)
}, [results]);
like image 77
lissettdm Avatar answered Oct 23 '22 06:10

lissettdm


Here is what you need (probably):

const [results, setResults] = useState({ /* YOUR STATE */ });

const { number } = props

const handleWorker = useCallback((number) => {
  // DO WHATEVER YOU NEED TO DO HERE
},[]); // IF YOU DEPEND ON ANY MUTABLE VARIABLES, ADD THEM TO THE DEPENDENCY ARRAY

useEffect(() => {
  // DO WHATEVER YOU NEED TO DO HERE
  // INSTEAD OF localStorage.setItem(number, results)
  // DO localStorage.setItem(number, { ...facto })
}, [number,handleWorker]);

Let me know if it works.

like image 1
cbdeveloper Avatar answered Oct 23 '22 05:10

cbdeveloper