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 ponent solution, but I am wondering if there is a simple react functional solution.
Here is the scenario:
- create a functional react ponent with. few states
- create several button elements that each alter a different state.
- using one of the button elements, trigger an async action.
- If there was any other change in the state, prior to receiving results from the async function, abort all other continuing actions.
Attached is a code sandbox example =/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=".6.3/umd/react.production.min.js"></script>
<script src=".6.3/umd/react-dom.production.min.js"></script>
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 ponent solution, but I am wondering if there is a simple react functional solution.
Here is the scenario:
- create a functional react ponent with. few states
- create several button elements that each alter a different state.
- using one of the button elements, trigger an async action.
- If there was any other change in the state, prior to receiving results from the async function, abort all other continuing actions.
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./ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Share
Improve this question
edited Jul 13, 2020 at 19:15
FZs
18.6k13 gold badges46 silver badges56 bronze badges
asked Jun 17, 2020 at 8:20
Shai KimchiShai Kimchi
7861 gold badge6 silver badges23 bronze badges
1
- I do want to keep counting but also trigger the alert('counter was changed') – Shai Kimchi Commented Jun 17, 2020 at 10:36
3 Answers
Reset to default 13 +50You 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
I've found another answer by asking the question on facebook-rect github. Apparently, since setting a state is a function, it's first argument is the current state.
so it is possible to have access to the previous value by using this snippet when setting the counter value:
setCounter(prevValue => {
alert(prevValue + " " + counter);
return counter + 1;
});
https://github./facebook/react/issues/19270 https://reactjs/docs/hooks-reference.html#functional-updates
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