2

How do I trigger a the useEffect hook when state.providers["providerId"].isProviderRowValid changes?

I cannot put state into the dependency array as it will cause an infinite loop because of the subsequent state update to isSubmitDisabled.

  const providerRowHandler = (providerId: string, isRowValid: boolean): void => {
    setState({
      ...state,
      providers: {
        ...state.providers,
        [providerId]: {
          ...state.providers[providerId],
          isProviderRowValid: isRowValid
        }
      }
    });
  };

  useEffect(() => {
    console.log('state.providers changed, update form state');
    const providers = { ...state.providers };
    const isRowsValid = Object.values(providers)
      .map(k => k.isProviderRowValid)
      .every(row => row === true);
    setState({
      ...state,
      isSubmitDisabled: isRowsValid === false
    });
  }, [// how to watch isProviderRowValid only as a state update here ]);
3
  • 1
    can't you just put state.providers["providerId"].isProviderRowValid in the dependency array? Commented Jun 30, 2020 at 20:41
  • Well then they need to be all of providers[id] in the dependency array as ["providerId"] is a dynamic key.. like [providers[0], providers[1] ...etc ] this doesnt work imo Commented Jun 30, 2020 at 20:47
  • providerId is not known at that point, it could be any key in state.providers Commented Jun 30, 2020 at 20:48

2 Answers 2

1

I think you can run whenever state.providers changed:

useEffect(() => {
  setState((state) => {
    console.log(
      'state.providers changed, update form state'
    );
    const isSubmitDisabled =
      Object.values(state.providers)
        .map((k) => k.isProviderRowValid)
        .every((row) => row === true) === false;
    if (isSubmitDisabled !== state.isSubmitDisabled) {
      //need to change state
      return {
        ...state,
        isSubmitDisabled,
      };
    }
    //do nothing
    return state;
  });
}, [state.providers]);
Sign up to request clarification or add additional context in comments.

7 Comments

My concern was that I did not want the effect to run when any other state.providers property other than isProviderRowValid is updated as there are several places that could trigger this effect.
@RyanP13 in your current answer you run the effect on every render.
how so? The effect will only run when a value of isProviderRowValid changes or am I wrong :)
@RyanP13 isProvidedRowValid is set by providerRowHandler so why not just set state.providers[providerId] there?
@RyanP13 in your answer the effect depends on getAllProviderRows and that function is re created on every render.
|
0
  function getAllProviderRows() {
    const providers = { ...state.providers };
    return Object.keys(providers).map(item => providers[item].isProviderRowValid);
  }

  useEffect(() => {
    const providers = { ...state.providers };
    const isRowsValid = Object.values(providers)
      .map(v => v.isProviderRowValid)
      .every(row => row === true);
    setState({
      ...state,
      isSubmitDisabled: isRowsValid === false
    });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [getAllProviderRows]);

I cannot log into my app currently but this should work

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.