简体   繁体   English

如何重新渲染已在 React 组件中删除的子 DOM 元素?

[英]How to re-render a child DOM element that has been deleted within a React component?

I have the following React component.我有以下 React 组件。 The top-level component is <App /> .顶级组件是<App />

Let's say the <div id="keyDiv"> element were to get deleted (line 10 of the code snippet below).假设<div id="keyDiv">元素将被删除(下面代码片段的第 10 行)。 What triggers this isn't really important to me - maybe another part of the App did it, or maybe a user did it via Chrome Devtools.是什么触发了这个对我来说并不重要 - 也许应用程序的另一部分做了它,或者用户通过 Chrome Devtools 做了它。 Either way, deleting <div id="keyDiv"> will effectively kill all the children of <Container> (at line 9).无论哪种方式,删除<div id="keyDiv">都会有效地杀死<Container>所有子项(在第 9 行)。

Is there any way to re-render the children of <Container> , so that those children will get re-generated?有没有办法重新渲染<Container>的孩子,以便这些孩子重新生成? (particularly <div id="keyDiv"><Modal/></div> ) (特别是<div id="keyDiv"><Modal/></div>

I have tried updating the state of Container, but this does not restore its children.我曾尝试更新 Container 的状态,但这并不能恢复其子项。

import React, {FC, useState} from 'react';
const App : FC = () => {
  const styles = {
    border:'1px solid black',
    backgroundColor: 'palegoldenrod',
    padding:'10px'
  }
  return (
    <Container>
      <div style={styles} id="keyDiv">
        <Modal />
      </div>
    </Container>
  )
}
const Container : FC = ({children}) => {
  const styles = {
    border:'1px solid black',
    padding:'10px'
  }
  const [label, setLabel] = useState('This is my label');
  return (
    <div>
      <div style={styles}>
        {label}
        {children}
      </div>
      <button onClick={() => {setLabel(`This is the date/time: ${new Date().toLocaleTimeString()}`)}}>Click me</button>
    </div>
  )
}
const Modal : FC = () => {
  const styles = {
    border:'1px solid black',
    padding:'10px',
    backgroundColor: 'lightgreen'
  }
  return (
    <div style={styles}>
      This should be the modal
    </div>
  )
}

You need to use MutationObserver .您需要使用MutationObserver Here's how I did it - some code with notes for each step:这是我的做法 - 一些带有每个步骤注释的代码:

import React, { useState } from 'react';

// Transfer to class component so I can use componentDidMount
class App extends React.Component {

  styles = {
    border:'1px solid black',
    backgroundColor: 'palegoldenrod',
    padding:'10px'
  }

  // When component mounts, create a reference to the div you want to target
  componentDidMount(){
    const immortalDiv = document.getElementById('keyDiv')
    // get a reference to its parent
    const immortalDivParent = immortalDiv.parentNode

    // config for your MutationObserver
    const MOconfig = { attributes: true, childList: true, subtree: true };

    // callback for your MutationObserver
    const callback = (mutationsList, observer) => {
      for(let mutation of mutationsList) {
        // check mutations in the childlist of the parent of the div you're trying to protect 
        //there's probably other things you can use to check that the mutation that occurs is the one you're trying to detect
        if (mutation.type === 'childList') {
          // if the childlist changes, you can run this code
          console.log('! Someone tried to remove your div - The Hamburgler perhaps !')
          // append your div back into its parent
          immortalDivParent.appendChild(immortalDiv)
          // diconnect the observer, otherwise you'll get stuck in an infinite loop
          observer.disconnect()
          // reconnect it right away to keep listening
          observer.observe(immortalDivParent, MOconfig)
        }
      }
    }

    const observer = new MutationObserver(callback)

    observer.observe(immortalDivParent, MOconfig)

  }


  render(){
    return (
      <Container>
        <div style={this.styles} id="keyDiv"><Modal /></div>
      </Container>
    )
  }
}

Now this is not a foolproof answer, as it will respond to any change in the parent div's children.现在这不是一个万无一失的答案,因为它会响应父 div 的孩子的任何变化。 But I'm sure a few more lines of code can help check for that, and then only re-render the div if that div was deleted.但我对于这些代码可以帮助检查肯定有几行,如果分区被删除,然后只重新呈现DIV。 This answer doesn't use a react re-render.这个答案不使用反应重新渲染。 I tried things with this.forceUpdate or using state and componentDidUpdate to make it work, but this seems the most straightforward.我尝试使用this.forceUpdate或使用 state 和componentDidUpdate使其工作,但这似乎是最直接的。 I'd be curious to know how to do it with React lifecycle methods.我很想知道如何使用 React 生命周期方法来做到这一点。

Here is a working codesandbox.是一个有效的代码沙盒。 If you open up the dev tools and delete your div, you'll see that the Hamburgler may have been the culprit, but the code puts it right back.如果您打开开发工具并删除您的 div,您会看到汉堡包可能是罪魁祸首,但代码将其放回原处。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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