The Interactive DOM
The Interactive DOM

Reputation: 85

useEffect to occur multiple times

I want the useEffect to occur multiple times. For example in the below example, everything works correctly the first time round.

If the input field is empty, and you click on 'Next', the focus then shifts to another button. When you click this button, the focus then shifts to the incomplete question. However this only works on the first render. How do I get it to work so that the useEffect occurs each time the button is clicked?

https://codepen.io/theinteractivedom/pen/ZExpqBy

const Summary = () => {
  const [hasErrors, setHasErrors] = useState(false);
  const [message, setMessage] = useState("");
  const goToQuestionButton = useRef();
  const inputRef = useRef();

  const handleClick = () => {
    if (message.trim().length !== 0) {
      console.log("input value is NOT empty");
    } else {
      setHasErrors(true);
    }
  };

  const handleClickTwo = () => {
    inputRef.current.focus();
  };

  const handleChange = (event) => {
    setMessage(event.target.value);
  };

  useEffect(() => {
    if (hasErrors === true) {
      goToQuestionButton.current.focus();
    }
  }, [hasErrors]);

  return (
    <div className='questions'>
      <p>What is your name?</p>
      <input
        onChange={handleChange}
        ref={inputRef}
        id="firstName"
        name="firstName"
      ></input>
      <button onClick={handleClickTwo} ref={goToQuestionButton}>
        Go to incomplete question
      </button>
      <button onClick={handleClick}>Next</button>
    </div>
  );

};

Upvotes: 1

Views: 409

Answers (4)

Tristan
Tristan

Reputation: 1790

useEffect will trigger any time the value of hasErrors changes. You never reset the hasErrors value after the input is focused.

useEffect(() => {
    if (hasErrors === true) {
      goToQuestionButton.current.focus();
      setHasErrors(false);
    }
  }, [hasErrors]);

Setting hasErrors to false after focusing the question will reset everything and allow it to be triggered again the next time hasErrors is set to true.

Upvotes: 2

Amandeep Singh
Amandeep Singh

Reputation: 848

You need to move goToQuestionButton.current.focus() inside handleClick function and remove from useEffect. like this:

    const handleClick = () => {
        if (message.trim().length !== 0) {
          console.log("input value is NOT empty");
        } else {
          setHasErrors(true);
          goToQuestionButton.current.focus()
        }
      };
     useEffect(() => {

     }, [hasErrors]);

Upvotes: 1

Tyler Dill
Tyler Dill

Reputation: 371

Right now you have [hasErrors] as a dependency for useEffect. That means that useEffect will only run if hasErrors changes. Right now you are only changing hasErrors to false in handleClick. On subsequent clicks, it is still false so useEffect doesn't think anything changed and therefore doesn't run. If you want it to run depending on hasErrors, you will need to keep changing hasErrors back and forth from true to false. But since inside useEffect you are checking for false only, you could instead just remove the dependency and let useEffect run every single render, like this:

  useEffect(() => {
    if (hasErrors === true) {
      goToQuestionButton.current.focus();
    }
  });

Upvotes: 1

emrich
emrich

Reputation: 258

The useEffect always triggers, when the state in the dependency array changes. If you set the state on true again, it does not change. Therefore, the useEffect is not triggered. You have to setHasErrors(false) after resolving the error or focusing the question Button.

Upvotes: 1

Related Questions