roasty
roasty

Reputation: 172

Is there a way to put requirements on hook dependencies?

Currently, I am checking if the input useEffect() hook dependency is at least a certain length before calling loadSearchData() which is an async method that hits an API.

  useEffect(() => {
    if (input.length >= MIN_CHAR_INPUT) {
      loadSearchData();
    }
  }, [input]);

Is there a way where I could move the input check to the input dependency param for useEffect()? Probably a case where I need to write a custom hook.

Upvotes: 0

Views: 42

Answers (1)

Thomas
Thomas

Reputation: 12637

Is there a way where I could move the input check to the input dependency param for useEffect()? Probably a case where I need to write a custom hook.

I'd build it this way:


function useEffect2(effect, deps, isValid = true) {
  const cleanup = React.useRef(null);

  useEffect(() => {
    if (isValid) {
      if (typeof cleanup.current === "function") {
      // schedule cancellation of previous request right after effect has been called
      // using the Promise construct here so I don't have to deal with a throwing cancellation function
        Promise.resolve().then(cleanup.current);
      }

      // in case effect() throws, 
      // don't want to call the old cancellation function twice
      cleanup.current = null;
      // get new cancel-function
      cleanup.current = effect();
    }
  }, deps);

  useEffect(() => () => {
    // deal with cancellation on unmount
    typeof cleanup.current === "function" && cleanup.current();
  }, []);
}

useEffect2(loadSearchData, [input], input.length >= MIN_CHAR_INPUT);

I just want to clarify the cancel. This will give us access to the current useEffect() call in the stack and allow us to properly handle the call without any memory-leaks

From https://reactjs.org/docs/hooks-effect.html#recap

We’ve learned that useEffect lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function

Cleanup is probably a better name. I use it the most to "cancel" previous ajax-requests if they are still pending/prevent them to update the state. I've renamed the variable in the code.

What we're trying to emulate here is a useEffect that runs the effect conditionally. So when the condition is false, we don't want the effect to cleanup the previous call; as if the deps didn't change. Therefore we need to handle the cleanup function ourselves, and when/wether it should be invoked. That's

  1. when (and only if) we call the effect function
  2. on componentWillUnmount

That's what this ref is for. Since the reference is overwritten with every call to effect this shouldn't leak any memory.

Upvotes: 1

Related Questions