3

I'm trying to create hook that is is using an effect in which side effect function returns the cleanup callback. However I want to call it only when component is unmounted, not on the rerender.

Normal approach when you call useEffect with empty deps array won't work here as the cleanup function is created only once, on the first call of the hook. But my clean up is created later, so there is no way to change it.


function useListener(data) {
  const [response, updateResponse] = useState(null);

  useEffect(
    () => {
      if (data) {
        const removeListener = callRequest(data, resp => {
          updateResponse(resp);
        });

        return removeListener;
      }
    },
    [data]
  );

  return response;
}

This comes down to a following problem: In normal class component, the willComponentUnmount could make a decision based on a current component state but in case of useEffect, state is passed via closure to the cleanup and there is no way to pass the information later if the state has changed

3
  • useEffect defines a way to make your component reacts to a state/props change. Instead of thinking in terms of lifecycles, it's better with hooks to think about what state could lead to running an effect. At least, that's what React thoughtleaders like Dan Abramov or Ryan Florence keep saying :) Commented May 14, 2019 at 8:55
  • Yes, I know, although I really need to do a once off cleanup based on the state and I can’t think of a way to do it Commented May 14, 2019 at 9:18
  • What cause the unmounting of the component ? Only the fact that data is not undefined ? Commented May 14, 2019 at 9:22

1 Answer 1

1

You can use useRef to save and update your callback function

The useRef() Hook isn’t just for DOM refs. The “ref” object is a generic container whose current property is mutable and can hold any value, similar to an instance property on a class. more

function useListener(data) {
  const [response, updateResponse] = useState(null);
  const cleanUpCallbackRef = useRef(() => {});

  useEffect(
    () => {
      if (data) {
        cleanUpCallbackRef.current = callRequest(data, resp => {
          updateResponse(resp);
        });
      }
    },
    [data]
  );

  useEffect(() => {
    return () => {
      cleanUpCallbackRef.current();
    }
  }, []);
  return response;
}

I create a simple example here

Sign up to request clarification or add additional context in comments.

1 Comment

thanks! This looks like exactly what I needed. I haven't thought of useRef, as I thought about it in context of DOM

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.