简体   繁体   中英

How to mutate the dom without breaking React

I'm using a library with a component that returns a bunch of HTML. Some of the HTML is ok, and some of it needs changed after rendering.

I'm aware this is unadvised however I am in a situation where this kind of hacky method seems necessary.

My problem is that following the dom mutations, React seems to break. Re-renders no longer occur, and there are no errors in the console to indicate why. This is obviously due to the mutations, but I think there must be some way to do this without breaking React. I've tried various things including using a ref and eg useLayoutEffect.

I have create a minimal-ish repro here:

https://codesandbox.io/s/busy-sea-mz04cr

couple of things here from your sandbox:

  • Re-renders are still occurring in both the App and MyComponent when you press the buttons, you can prove it by adding a console.log("XXX has re-rendered") in each component
  • Updating the DOM via a ref will not cause React to re-render
  • Your custom hook will always only ever execute once (when MyComponent mounts) because the dependencies never change
  • Your state changes (button presses) are still firing events and updating state but your DOM update via ref removes the rendering of the data variable

Going from

<div id="data">
    <p>Data: {data}</p>
</div>

to

<div id="data">
   <p>Data: mutation one</p>
</div>

So every button press, your <MyComponent /> is updating and rendering the same static JSX.

Here's a modified sandbox that passes the data state variable to the effect to cause it to trigger on button change + appendChild via ref instead replaceChild so we can see the re-renders

Some things that might be helpful:

  • Updating the DOM via refs will undoubtedly lead to problems, maybe you should parse the HTML that is returned from your mentioned library and render it with dangerouslySetInnerHTML (assuming it's safe to do so and is from a trusted source otherwise it opens potential exploits)
<div dangerouslySetInnerHTML={{__html: data}} />
function MyComponent() {
  const [data, setData] = useState("");

  const handleButtonPressed = (html) => {
    const fixedHtml = // ... make your changes to the HTML
    setData(fixedHtml);
  };

  return (
    <div>
      <button onClick={handleButtonPressed}>some button</button>
      <p>{data}</p>
    </div>
  );
}
}

Hope that helps, the new version of the React docs is a fantastic resource.

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