简体   繁体   中英

Is there any way to perform a cleanup function before the new render on a functional component

Description

I have SomeComponent which uses a some library (we'll call domLib ) that mutates the DOM directly (eg modifies and adds several elements & listeners). SomeComponent also accepts props data which domlib uses

const SomeComponent = ({ data }) => {
    useEffect(() => {
        domLib.init(data, 'example-container');
        return () => { //cleanup function
            domLib.destroy('example-container'); //removes everything from example-container, listeners etc from example-container
        }
    },[data])
    return (<div id='example-container'>
        <HelpComponent data={data}/>
    </div>)
}

domLib must also be able to see/modify HelpComponent or the children of ' example-container '

  • SomeComponent accepts the props, data
  • A child of SomeComponent named HelpComponent uses the prop data
  • A library domLib uses both the data and HelpComponent to mutate the DOM (children of the ' example-container ')

Problem

When receiving new props/updating, since the cleanup function runs AFTER render, the domLib.destroy('example-container'); removes the children of example-container which means <HelpComponent data={data}/> will also be removed

Attempts and Issues

useEffect(() => {
    domLib.destroy('example-container');
    ReactDom.unmountComponentAtNode(document.getElementById('example-container'))
    ReactDom.render(<HelpComponent data={data}/>, document.getElementById('example-container'))
    domLib.init(data, 'example-container');
})
return (<div id='example-container'>
    {/* removed */}
</div>)

Attempt , Create everything inside the useEffect hook using ReactDom and remove everything I need to right before it.

Issue , the render method in ReactDom.render(<HelpComponent data={data}/>, document.getElementById('example-container')) does not immediately apply changes to the DOM therefore when domLib cannot see HelpComponent

Edit for extra clarity

The library domLib is jsPlumb which I am using to create links between elements. The elements are created based on the data prop, and the lib jsPlumb creates links between those elements.

The issue is the elements created from jsPlumb are outside of the react ecosystem, so it remains between updates, therefore I need to remove them between updates

Add [] as a second argument for useEffect so it would run once. If you need any props for your lib, add them to watch after changes. Your hook would run only then. For more great information, written by Dan: https://overreacted.io/a-complete-guide-to-useeffect/ or docs: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect

EDIT: to watch certain props changes, add them to the square brackets in useEffect . An example from the docs:

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

If you want to call destroy method just once, not depending on data changes, do this:

useEffect(
  () => {
    return () => {
      subscription.unsubscribe();
    };
  },
  [],
);

Empty function body would do nothing on the first run, but the returned function would run on destroy.

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