40

I've got a may confusing question because it does not fit standard-behaviour how react and the virtual dom works but i would like to know the answer anyway.

Imagine i've got a simple react-component which is called "Container". The Container-component has a "div" inside of the render-method which contains another component called "ChildContainer". The "div" which surrounds the "ChildContainer" has the id "wrappingDiv".

Example:

render() {
  <Container>
    <div id="wrappingDiv">
      <ChildContainer/>
    </div>
  </Container
}

How can i destroy the "ChildContainer"-component-instance and create a completly new one. Which mean the "ComponentWillUnmount" of the old instance is called and the "ComponentDidMount" of the new component is called.

I don't want the old component to update by changing the state or props.

I need this behaviour, because an external library from our partner-company got a libary which change the dom-items and in React i'll get a "Node not found" exception when i Update the component.

0

3 Answers 3

104

If you give the component a key, and change that key when re-rendering, the old component instance will unmount and the new one will mount:

render() {
  ++this.childKey;
  return <Container>
    <div id="wrappingDiv">
      <ChildContainer key={this.childKey}/>
    </div>
  </Container>;
}

The child will have a new key each time, so React will assume it's part of a list and throw away the old one, creating the new one. Any state change in your component that causes it to re-render will force that unmount-and-recreated behavior on the child.

Live Example:

class Container extends React.Component {
  render() {
    return <div>{this.props.children}</div>;
  }
}

class ChildContainer extends React.Component {
  render() {
    return <div>The child container</div>;
  }
  componentDidMount() {
    console.log("componentDidMount");
  }
  componentWillUnmount() {
    console.log("componentWillUnmount");
  }
}

class Example extends React.Component {
  constructor(...args) {
    super(...args);
    this.childKey = 0;
    this.state = {
      something: true
    };
  }

  componentDidMount() {
    let timer = setInterval(() => {
      this.setState(({something}) => ({something: !something}));
    }, 1000);
    setTimeout(() => {
      clearInterval(timer);
      timer = 0;
    }, 10000);
  }
  
  render() {
    ++this.childKey;
    return <Container>
      {this.state.something}
      <div id="wrappingDiv">
        <ChildContainer key={this.childKey}/>
      </div>
    </Container>;
  }
}

ReactDOM.render(
  <Example />,
  document.getElementById("root")
);
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.2/umd/react-dom.production.min.js"></script>


Having said that, there may well be a better answer to your underlying issue with the plugin. But the above addresses the question actually asked... :-)

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

Comments

11

Using hooks, first create a state variable to hold the key:

const [childKey, setChildKey] = useState(1);

Then use the useEffect hook to update the key on render:

useEffect(() => {
   setChildKey(prev => prev + 1);
});

Note: you probably want something in the array parameter in useEffect to only update the key if a certain state changes

Comments

0

You can just pass a key prop in the component you want to re-render as it'll re-render once the contents of the key change.

<Component
key={anystate/ref} // for serious usecases you should be passing a controlled state
...other props
/>

This should work!

Comments

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.