简体   繁体   中英

React useEffect cleanup function depends on async await result

I have a react component which gets some resource asynchronously and then subscribes to resource changes.

The problem is that the cleanup function is not in the closure of this resource:

useEffect(() => {
  const onResourceChange = () => { 
    console.log('resource changed');
  };

  getSomeResource().then(resource => {
    resource.subscribe(onResourceChange);
  });

  return () => {
    resource.unsubscribe(onResourceChange); // Error! resource is undefined
  }
}, []);

Since using async functions in useEffect is not allowed, what is the best way to unsubscribe from this resource inside useEffect 's cleanup function?

I see two "side effects" going on here.

  1. Getting the resource (need happen once, dependency [] )
  2. Subscribe/unsubscribing the callback (needs to happen on resource change, dependency, [resource] )

So I

  1. separated them into two "effects" (steps, each handling one side effect)
  2. and extract it into a custom hook
    function useResource() {
        const [resource, setResource] = useState(undefined)

        const onResourceChange = () => console.log('resource changed');

        // Get the resource, initially.
        useEffect(() => {
          getSomeResource(setResource)
        }, [])

        // When the resource is retrieved (or changed),
        // the resource will subscribe and unsubscribe
        useEffect(() => {
          resource.subscribe(onResourceChange)
          return () => resource.unsubscribe(onResourceChange)
        }, [resource])
    }

    // Use it like this
    function App() {
        useResource()

        return <>your elements</>
    }

As you can see, the first useEffect is responsible for fetching the "resource" only, while the 2nd useEffect is responsible for un/subscribing the callback.

And check out the deps list in each useEffect (The former is empty [] while the latter depends on [resource] )

This is a solution, probably the ugly one... but still a solution :)

useEffect(() => {
    const onResourceChange = () => { 
        console.log('resource changed');
    };

    let data = {};

    getSomeResource().then(resource => {
        data.resource = resource;
        resource.subscribe(onResourceChange);
    });

    return () => {
        data.resource && data.resource.unsubscribe(onResourceChange); // Error! resource is undefined
    };
}, []);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM