How to get the changed state after an async action, using React functional hooks? I have found a redux solution for this issue, or a react class component solution, but I am wondering if there is a simple react functional solution.
Here is the scenario:
Attached is a code sandbox example https://codesandbox.io/s/magical-bird-41ty7?file=/src/App.js
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [counter, setCounter] = useState(0);
const [asyncCounter, setAsyncCounter] = useState(0);
return (
<div className="App">
<div>
<button
onClick={async () => {
//sets the asyncCounter state to the counter states after a 4 seconds timeout
let tempCounter = counter;
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 4000);
});
if (tempCounter !== counter) {
alert("counter was changed");
} else {
setAsyncCounter(counter);
}
}}
>
Async
</button>
<label>{asyncCounter}</label>
</div>
<div>
<button
onClick={() => {
//increases the counter state
setCounter(counter + 1);
}}
>
Add 1
</button>
<label>{counter}</label>
</div>
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You could possibly use a React Ref to store the state of the state variable. Then update the state variable with the react ref. This will render a page refresh, and then use the React Ref in the async function.
In class-based React components, we can pass a callback into the 2nd argument of setState to run code when a state is updated with setState . With React hooks, we no longer have the setState method. Instead, we use state updater functions created with the useState hook to update states.
Let’s look at how to do that with hooks. The first and most commonly used method to run a function after updating state is the useEffect hook. useEffect runs its function only when the items in the dependency array change. Sounds pretty spot on for what we want huh?
Now, because React Tracked is a wrapper around React Hooks and Context, it doesn’t support async actions natively. This post shows some examples how to handle async actions. It’s written for React Tracked, but it can be used without React Tracked. The example we use is a simple data fetching from a server.
You can use a ref to keep track of the counter value independently
const [counter, setCounter] = useState(0);
const counterRef = useRef(counter)
Whenever you update counter you update counterRef as well:
const newCounter = counter + 1
setCounter(newCounter);
counterRef.current = newCounter
And then check it:
if (counterRef.current !== counter) {
alert("counter was changed");
} else {
setAsyncCounter(counter);
}
Codesandox
As @thedude mentioned, you will need to use the useRef
hook – it was made exactly for your use case, as the docs say: "It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes."
I think you might just want to add a simple boolean:
const counterChanged = useRef(false);
and then when you update the counter, you update this too.
counterChanged.current = true;
setCounter(counter + 1);
and inside your async function, you set it to false and then check if it's been changed.
counterChanged.current = false;
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 4000);
});
if (counterChanged.current) {
// alert
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