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
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.
React do not update immediately, although it seems immediate at first glance.
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.
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.
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.
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.
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
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]);
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With