How to force remounting on React components?

Asked
Active3 hr before
Viewed126 times

10 Answers

componentsreact
90%

To remount a component when a prop changes, use the React key attribute as described in this post on the React blog:,The example below shows how the key attribute can be used. In Parent, the key attribute of <Child> is set to String(primaryExists). When primaryExists changes in the parent component, the child component unmounts and remounts allowing useState to re-initialize its state with the intial value passed in from props (!primaryExists). Play with it on CodeSandbox.,Sebastian Markbåge, from the React core team, reinforced the usage of keys on single items:

import React, { useState } from "react";

function Parent() {
  // Note: in my real code, primaryExists was derived from API data,
  // but I useState here to simplify the example
  const [primaryExists, setPrimaryExists] = useState(true);
  return (
    <div>
      <label>
        <input
          checked={primaryExists}
          onChange={() => setPrimaryExists(x => !x)}
          type="checkbox"
        />
        Primary exists
      </label>
      <Child key={String(primaryExists)} primaryExists={primaryExists} />
    </div>
  );
}

function Child({ primaryExists }) {
  const [isPrimary, setIsPrimary] = useState(!primaryExists);
  return (
    <div>
      <label>
        <input
          checked={isPrimary}
          onChange={() => setIsPrimary(x => !x)}
          type="checkbox"
        />
        Is primary
      </label>
    </div>
  );
} 
88%

Thanks for contributing an answer to Stack Overflow!,Join Stack Overflow to learn, share knowledge, and build your career., Stack Overflow for Teams Where developers & technologists share private knowledge with coworkers

For example:

render(){
    if (this.state.employed) {
        return (
            <div key="employed">
                <MyInput ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div key="notEmployed">
                <MyInput ref="unemployment-reason" name="unemployment-reason" />
                <MyInput ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}

OR

render(){
    if (this.state.employed) {
        return (
            <div>
                <MyInput key="title" ref="job-title" name="job-title" />
            </div>
        );
    } else {
        return (
            <div>
                <MyInput key="reason" ref="unemployment-reason" name="unemployment-reason" />
                <MyInput key="duration" ref="unemployment-duration" name="unemployment-duration" />
            </div>
        );
    }
}
load more v
72%

That's quite interesting! By changing the key on a component we can force it to remount.,In an App component we can use the Item. Every time you click on the button the string passed into key is updated.,In our App component we included the Detail component like this:

We create a component Item with a useEffect logging out when the component mounts and unmounts. We achieve this with an empty dependency array.

const Item = () => {  useEffect(() => {    console.log("Mount item");    return () => console.log("Unmount item");  }, []);  return <div />;};
const Item = () => {  useEffect(() => {    console.log("Mount item");    return () => console.log("Unmount item");  }, []);  return <div />;};

In an App component we can use the Item. Every time you click on the button the string passed into key is updated.

const App = () => {  const [id, setId] = useState("123");  return (    <>      <Item key={id} />      <button onClick={() => setId(Math.random().toString())}>        update      </button>    </>  );};
const App = () => {  const [id, setId] = useState("123");  return (    <>      <Item key={id} />      <button onClick={() => setId(Math.random().toString())}>        update      </button>    </>  );};

Initially we built it in a way that a Detail component would have local state, which is based on the initial props. Let me illustrate this by a simplified example. Here the default value of useState is based on the prop contact.name.

const Detail = (props) => {  const [name, setName] = useState(props.contact.name);  return (    <form>      <input        value={name}        onChange={(evt) => setName(evt.target.value)}      />    </form>  );};
const Detail = (props) => {  const [name, setName] = useState(props.contact.name);  return (    <form>      <input        value={name}        onChange={(evt) => setName(evt.target.value)}      />    </form>  );};

In our App component we included the Detail component like this:

function App() {  const [contacts, setContacts] = React.useState([    { id: "a", name: "Anna" },    { id: "b", name: "Max" },    { id: "c", name: "Sarah" },  ]);  const [activeContactId, setActiveContactId] = React.useState(    "a"  );  const activeContact = contacts.find(    (entry) => entry.id === activeContactId  );  return (    <>      {contacts.map((contact) => (        <button          key={contact.id}          onClick={() => setActiveContactId(contact.id)}        >          {contact.name}        </button>      ))}      <Detail contact={activeContact} />    </>  );}
function App() {  const [contacts, setContacts] = React.useState([    { id: "a", name: "Anna" },    { id: "b", name: "Max" },    { id: "c", name: "Sarah" },  ]);  const [activeContactId, setActiveContactId] = React.useState(    "a"  );  const activeContact = contacts.find(    (entry) => entry.id === activeContactId  );  return (    <>      {contacts.map((contact) => (        <button          key={contact.id}          onClick={() => setActiveContactId(contact.id)}        >          {contact.name}        </button>      ))}      <Detail contact={activeContact} />    </>  );}

So we changed

<Detail contact={activeContact} />
<Detail contact={activeContact} />

to

<Detail key={activeContact.id} contact={activeContact} />
<Detail key={activeContact.id} contact={activeContact} />
load more v
65%

Spectrum will become read-only on August 24, 2021. Learn more about the decision in our official announcement.

export class Example extends React.Component {  state = {    name: ''  }  componentDidMount() {    this.handleUpdateName();  }  componentDidUpdate(prevProps, prevState) {    if (prevState.name !== this.state.name) {      this.handleUpdateName();    }  }  handleUpdateName = () => {    this.setState({ name: this.props.name })  }  render() {    return <h1>{this.state.name}</h1>  }}
load more v
75%

It is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same. Just to be clear, rerender in this context means calling render for all components, it doesn’t mean React will unmount and remount them. It will only apply the differences following the rules stated in the previous sections.,Because React relies on heuristics, if the assumptions behind them are not met, performance will suffer.,When diffing two trees, React first compares the two root elements. The behavior is different depending on the types of the root elements.

<div>
   <Counter />
</div>

<span>
   <Counter />
</span>
load more v
40%

In fact, the way this is written, the ShoeList would never refresh, as we are not dependent on props for rendering. Let’s fix that.,And boom — our child component has been “forced” to update.,In the React world, forcing a re-render is frowned upon. You should let the DOM take care of itself when React perceives changes to state or props. In order to follow these patterns, we sometimes have to do stuff that seems a little silly. Consider this scenario:

const UserShow extends Component {
  state = {
    user: {}
  }
  
  componentDidMount() {
    this.fetchUser().then(this.refreshUser)
  }
  
  setNewColor = color => {
    this.updateUser({color}).then(this.refreshUser)
  }
  
  refreshUser = res => this.setState({user: res.data.user})
  
  render() {
    const { user } = this.state;
    
    return (
      <div>
        User name: {user.name}
        Pick color: 
        <div>
          {colors.map(color => 
            <div className={color} 
                 onClick={() => this.setNewColor(color)} />)}
          )}
        </div>
        <ShoeList id={user.id} />
      </div>
    )
  }
}
load more v
22%

You can make sure to have a parent component render the component with different keys, this forces a recreation.,Thanks for the "yak shaving".,But you are yak shaving a bit here, trying to work around the problems you set up for yourself by requesting in componentDidMount.

Pretag
 Pretag team - issue, fix, solve, resolve
60%

Then after one second (to simulate a user-triggered animation), Container will render the second child component View21, which is structurally identical to View1. I also strategically added log messages to a few lifecycle methods of both components to help you follow along.,Here I present a minimal, complete, and verifiable example of the problem that I ran into. I have a Child element that renders to an HTML button (styled by Bootstrap for ease of viewing) with a numeric label for demo purposes. I also have a Container component that, on launch, will render the first child component View1 1. View1 simply wraps two Child elements with ids 1 and 2:,This whole example might seem contrived but has significant consequence for animation with React’s transition API. Suppose in View1, two buttons are spaced some distance apart and in View2, they move closer together through a CSS transform transition3.

Here I present a minimal, complete, and verifiable example of the problem that I ran into. I have a Child element that renders to an HTML button (styled by Bootstrap for ease of viewing) with a numeric label for demo purposes. I also have a Container component that, on launch, will render the first child component View1 1. View1 simply wraps two Child elements with ids 1 and 2:

1<View1>2  <Child id={1}/>3  <Child id={2}/>4</View1>
1<View1>2  <Child id={1}/>3  <Child id={2}/>4</View1>

Because the “virtual DOM” structure is the same between the two views:

1<Container>2  <Child id={1}/>3  <Child id={2}/>4</Container>
1<Container>2  <Child id={1}/>3  <Child id={2}/>4</Container>

I expected React to not unmount and then remount the two DOM nodes corresponding to the two buttons when switching from View1 to View2. However, as the console.log messages2 show:

1 "Child 1 has been mounted"
2 "Child 2 has been mounted"
3 "Container mounted"
4 "Will change viewIndex to 2 in one second."
5 "Child 1 has been unmounted"
6 "Child 2 has been unmounted"
7 "Child 1 has been mounted"
8 "Child 2 has been mounted"
9 "Container's state has been updated to 2 and finished re-render."
1 "Child 1 has been mounted"
2 "Child 2 has been mounted"
3 "Container mounted"
4 "Will change viewIndex to 2 in one second."
5 "Child 1 has been unmounted"
6 "Child 2 has been unmounted"
7 "Child 1 has been mounted"
8 "Child 2 has been mounted"
9 "Container's state has been updated to 2 and finished re-render."

as shown by the console log:

1 "Child 1 has been mounted"
2 "Child 2 has been mounted"
3 "Container mounted"
4 "Will change viewIndex to 2 in one second."
5 "Container's state has been updated to 2 and finished re-render."
1 "Child 1 has been mounted"
2 "Child 2 has been mounted"
3 "Container mounted"
4 "Will change viewIndex to 2 in one second."
5 "Container's state has been updated to 2 and finished re-render."

And what have I changed? Just two lines! I just changed how the JSX is generated. Here’s the diff going from the first to the second implementation:

1@@ -62,9 +62,9 @@2   render() {3     const {viewIndex} = this.state;4     if (viewIndex === 1) {5-      return <View1 key='index'/>6+      return View1()7     } else {8-      return <View2 key='index'/>9+      return View2()10     }11   }12 }
1@@ -62,9 +62,9 @@2   render() {3     const {viewIndex} = this.state;4     if (viewIndex === 1) {5-      return <View1 key='index'/>6+      return View1()7     } else {8-      return <View2 key='index'/>9+      return View2()10     }11   }12 }

Basically, in the first implementation, from React’s point of view, it has to reconcile this virtual DOM tree:

1<Container>2
   <View1 key='view' />3
   <Container />
1<Container>2
   <View1 key='view' />3
   <Container />

with this tree:

1<Container>2
   <View2 key='view' />3
</Container>
1<Container>2
   <View2 key='view' />3
</Container>

On the other hand, in the second implementation, React has to reconcile this:

1<Container>2  <div>3    <Child id={1} key='child-1'/>4    <Child id={2} key='child-2'/>5  </div>6<Container>
1<Container>2  <div>3    <Child id={1} key='child-1'/>4    <Child id={2} key='child-2'/>5  </div>6<Container>

with this:

1<Container>2  <div>3    <Child id={1} key='child-1'/>4    <Child id={2} key='child-2'/>5  </div>6</Container>
1<Container>2  <div>3    <Child id={1} key='child-1'/>4    <Child id={2} key='child-2'/>5  </div>6</Container>

This whole example might seem contrived but has significant consequence for animation with React’s transition API. Suppose in View1, two buttons are spaced some distance apart and in View2, they move closer together through a CSS transform transition3.

1function View1() {2  return (3    <div>4      <Child id={1} key='child-1' x={20} y={0}/>5      <Child id={2} key='child-2' x={200} y={0}/>6    </div>7  )8}9function View2() {10  return (11    <div>12      <Child id={1} key='child-1' x={60} y ={0}/>13      <Child id={2} key='child-2' x={120} y={0}/>14    </div>15  )16}
1function View1() {2  return (3    <div>4      <Child id={1} key='child-1' x={20} y={0}/>5      <Child id={2} key='child-2' x={200} y={0}/>6    </div>7  )8}9function View2() {10  return (11    <div>12      <Child id={1} key='child-1' x={60} y ={0}/>13      <Child id={2} key='child-2' x={120} y={0}/>14    </div>15  )16}
load more v
48%

You mount the component on startup, even if the user never accesses it. Mounting multiple components at the same time can accumulate to very sluggish start-up performance.,An interesting option since it leaves the && conditional rendering pattern intact:,You update the component even when it’s invisible, which may or may not be what you want.

1
load more v
23%

We can flip this on its head and utilize key as a way of forcing a remount and re-render of a child component.,There are a couple prop names that have reserved usage in React. One of those is key. We generally use key when we are rendering a list of things. It is a way of uniquely identifying each element in a list so that React minimizes re-rendering when parts of the list change.,Imagine I have a component that does a number of things including rendering a component with some internal state, such as a counter.

Imagine I have a component that does a number of things including rendering a component with some internal state, such as a counter.

class MyComponent extends React.Component {
  state = {
    remountKey: (new Date()).getTime(),
  }

  resetCounter = () => {
    this.setState({
      remountKey: (new Date()).getTime(),
    });
  }

  render() {
    return (
      <div>
        {/* some other stuff in my component */}

        <Counter key={this.state.remountKey} />
        <button onClick={this.resetCounter}>Reset Counter</button>
      </div>
    );
  }
}

Other "components-react" queries related to "How to force remounting on React components?"