And what about this?
useEffect(() => {
if (val) {
// do stuff
}
}, [val]);
Answer from Max on Stack Overflowreactjs - useState with boolean value in react - Stack Overflow
Hook is not triggered when boolean property changed on nested object.
why boolean variable is passed to useState() is always false? React hooks
How to check if state default boolean false is changing to true with React Hook useEffect
Can’t we just use the prop value directly instead of wrapping it in a useEffect?
Say,
Component = ({bool}) => {
If (bool) playSound()
return … }
Versus:
…
UseEffect(() => if(bool) playSound(), [bool])
…
These two cases have same behavior.
Update: It’s better to use useEffect, which only fires the side effect when the dependency changes when a component re-renders. It guards against incorrectly triggering the side effect if the dependency did not change value upon component re-render.
setIsLoading is an async function and you cannot get the state value immediately after update.
setState actions are asynchronous and are batched for performance gains. setState() does not immediately mutate this. Thus the setState calls are asynchronous as well as batched for better UI experience and performance. This applies on both
functional/Classcomponents.
From React documentation
React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. You could read more about this here
If you want to get the updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.
const {useEffect, useState } = React;
const App = (props) => {
const [isLoading, setIsLoading] = useState(false)
const buttonHandler = () => {
setIsLoading(current => !current)
}
useEffect( () => {
console.log(isLoading);
}, [isLoading]);
return (
<div>
<button onClick={buttonHandler} type="button">
Change
</button>
{isLoading? "Loading...": null}
</div>
)
}
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root">
loading.....
</div>
This is the expected behavior. You may want to use useEffect to access the latest value.
Here is a thread discussing the same issue: useState set method not reflecting change immediately
Hope this helps!
You can add another useEffect which watches this change, useEffect takes a second argument which is dependency array and the effect gets called if any of the dependency array value changes .
In this case since you need to make a decision based on the nomStatus, you can add it as a dependency to your useEffect
useEffect(() => {
if (nomStatus) {
setShowCalender(true);
}
}, [nomStatus]);
You can't since React state updates are asynchronously processed, the nomStatus state update won't be available until the next render cycle. Use the res.data[0].status value to set the showCalendar state.
const [nomStatus, setNomStatus] = useState(false);
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
if (res.data[0].status){
setShowCalender(true);
}
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
Or you can use a second useEffect hook with a dependency on nomStatus state update to set the showCalendar state.
useEffect(() => {
const fetchData = async () => {
const email = localStorage.getItem("loginEmail");
try {
const res = await Axios.get(
"http://localhost:8000/service/activeStatus",
{email}
);
setNomStatus(res.data[0].status);
console.log("Get status data :" + res.data[0].status);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
useEffect(() => {
if (nomStatus){
setShowCalender(true);
}
}, [nomStatus]);
EDIT 2022-07-30
After reviewing the codesandbox you shared, it seems you are approaching your state organization incorrectly.
In your case, the parent component calls some API and, if the API returns an error, you want to show an error box as a child component. As soon as the user exits the error box, you want to hide the error box.
In this case, you should not store the state of whether the error box is showing within the child component. Otherwise, the parent will not know whether the child is showing or not, and thus it will not be able to correctly trigger a re-show of the ErrorBox on new validation errors.
Instead, you should store that boolean flag within the parent. Once the API call returns an error, you set the flag to true and show the error box. Within the error box, you should pass an onClose function that, when invoked, toggles the boolean state to false within the parent, which triggers the parent to hide the error box. This way, the error box is not storing whether it is hidden or not (which was causing the inconsistency issue you see in your sandbox).
I have fixed your sandbox accordingly at this url: https://codesandbox.io/s/so-q-73146681-6854489-demo-fixed-94pki1?file=/src/App.js
I would also highly recommend that you read some of the principles that the React documentation lays out regarding handling state and lifting state up:
https://reactjs.org/docs/state-and-lifecycle.html
https://reactjs.org/docs/lifting-state-up.html
(Although the above articles use class components, the principles apply to the useState hook as well)
Here are the relevant snippets:
App.js:
export default function App() {
const [error, setError] = useState("");
const [showErrorBox, setShowErrorBox] = useState(false);
const callApi = () => {
setError("error");
setShowErrorBox(true);
};
return (
<div className="App">
<button onClick={callApi}>Throw Error</button>
{showErrorBox && (
<ErrorBox errorData={error} onClose={() => setShowErrorBox(false)} />
)}
</div>
);
}
ErrorBox.js:
const ErrorBox = ({ errorData, onClose }) => {
console.log("ERROR DATA", errorData);
return (
<>
<div
className="alert alert-warning alert-dismissible fade show"
role="alert"
>
<strong>Error!</strong>
<button
type="button"
className="close"
data-dismiss="alert"
aria-label="Close"
onClick={onClose}
>
<span aria-hidden="true">×</span>
</button>
</div>
</>
);
};
export default ErrorBox;
As a final thought, rather than calling the useState hook and just storing a boolean, I would recommend checking out the useBoolean hook within the usehooks-ts library. It gives you setTrue and setFalse convenience methods by default which can help condense your boolean-toggling code, in addition to many other great hooks that will help you out as you progress in React development.
Original answer
Like krishn kumar modanval said in his comment, you should pass error as a dependency to the dependencies array in useEffect:
useEffect(() => {
if(error) {
setShow(true)
}
}, [error]) // <-- error added here
I recommend checking out the useEffect documentation here, especially the "Optimizing Performance" section where it discusses how the dependencies array works.
(If this solution alone doesn't work for you, we'll need to see how the error prop that you're passing in works with the alert box code that you're showing us. Please create a stackblitz/plunkr with an MVE demonstrating your problem and we can help you debug it better.)
it's simple follow this:
onClick={() => setShow(!show)}
this will toggle your state Value every click.