Prakash Patil
Prakash Patil

Reputation: 277

Boolean value doesn't change in useState

An Alert component is showed on invalid credentials of user input. It also has its specific close button to hide the component, it works fine only once, which shows boolean value true and when closed its false, but, when invalid credentials are inputted again then it doesn't change value and remains false as it is. Please refer to the code as

const [show, setShow] = useState(true);

useEffect(() => {
    if(error) {
      setShow(true)
    }
}, [])

        return ( 
            <>
            {
                show ?
                <div className="alert alert-warning error" role="alert">
                    <i className="bi x-lg" onClick={() => setShow(false)}></i>
                    <strong>ERROR</strong>
                </div> : null
             }
                </>
        )

As you can see I'm putting the condition when 'error' is triggered which I receive from props. If its true then the alert box should render and when closed on cancel icon should close it. It works only first time, as the value for state is false and remains as it is even though 'error' is true in useEffect.

I want to show the alert box when 'error' is true and close on click of cancel icon and show it back when 'error' is true again. What could be the best solution?

Upvotes: 0

Views: 2633

Answers (2)

takanuva15
takanuva15

Reputation: 1678

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">&times;</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.)

Upvotes: 3

Sujeet kumar
Sujeet kumar

Reputation: 66

it's simple follow this:

onClick={() => setShow(!show)}

this will toggle your state Value every click.

Upvotes: 0

Related Questions