83

The Parent (MyList in my example) component renders an array thru a Child (MyComponent) component. Parent decides to change properties in the array, what is React way of triggering child re-rendering?

All I came up with is this.setState({}); in Parent after tweaking the data. Is this a hack or a React way of triggering an update?

JS Fiddle: https://jsfiddle.net/69z2wepo/7601/

var items = [
  {id: 1, highlighted: false, text: "item1"},
  {id: 2, highlighted: true, text: "item2"},
  {id: 3, highlighted: false, text: "item3"},
];

var MyComponent = React.createClass({
  render: function() {
    return <div className={this.props.highlighted ? 'light-it-up' : ''}>{this.props.text}</div>;
  }
});

var MyList = React.createClass({
  toggleHighlight: function() {
    this.props.items.forEach(function(v){
      v.highlighted = !v.highlighted;
    });

    // Children must re-render
    // IS THIS CORRECT?
    this.setState({});
  },

  render: function() {
    return <div>
      <button onClick={this.toggleHighlight}>Toggle highlight</button>
      {this.props.items.map(function(item) {
          return <MyComponent key={item.id} text={item.text} highlighted={item.highlighted}/>;
      })}
    </div>;
  }
});

React.render(<MyList items={items}/>, document.getElementById('container'));

6 Answers 6

48

The problem here is that you're storing state in this.props instead of this.state. Since this component is mutating items, items is state and should be stored in this.state. (Here's a good article on props vs. state.) This solves your rendering problem, because when you update items you'll call setState, which will automatically trigger a re-render.

Here's what your component would look like using state instead of props:

var MyList = React.createClass({
    getInitialState: function() {
        return { items: this.props.initialItems };
    },

    toggleHighlight: function() {
        var newItems = this.state.items.map(function (item) {
            item.highlighted = !item.highlighted;
            return item;
        });

        this.setState({ items: newItems });
    },

    render: function() {
        return (
            <div>
                <button onClick={this.toggleHighlight}>Toggle highlight</button>
                { this.state.items.map(function(item) {
                    return <MyComponent key={item.id} text={item.text} 
                             highlighted={item.highlighted}/>;
                }) }
            </div>
        );    
    }
});

React.render( <MyList initialItems={initialItems}/>,
              document.getElementById('container') );

Note that I renamed the items prop to initialItems, because it makes it clear that MyList will mutate it. This is recommended by the documentation.

You can see the updated fiddle here: https://jsfiddle.net/kxrf5329/

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

3 Comments

I've been pulling my hair for days now because I didn't know what was the best way of doing this. I knew that state <- props is an anti-pattern and I didn't know the documentations states that it CAN be okay to do it in cases like this one. Thanks for pointing it out!
"Both props and state changes trigger a render update" from your article link github.com/uberVU/react-guide/blob/master/props-vs-state.md
Sometimes you need to use data from outside the component where the state will not work. In my case, it's an external stylesheet. Even this.forceUpdate() is not working.
43

An easy option to re-render a child is to update a unique key attribute every time you need a re-render.

<ChildComponent key={this.state.updatedKey}/>

3 Comments

Simple and best answer and super logical REACTive way. Thanks +rep
can updatedKey be number? or key attribute can be any value?
@Dee It will be any but make sure it will be unique.
35

I have found a nice solution using key attribute for re-render with React Hook. If we changed key property of a child component or some portion of React Component, it will re-render entirely. It will use when you need to re-render some portion of React Component of re-render a child component. Here is a example. I will re-render the full component.

import React, { useState, useEffect } from "react";
import { PrEditInput } from "./shared";

const BucketInput = ({ bucketPrice = [], handleBucketsUpdate, mood }) => {
  const data = Array.isArray(bucketPrice) ? bucketPrice : [];
  const [state, setState] = useState(Date.now());
  useEffect(() => {
    setState(Date.now());
  }, [mood, bucketPrice]);
  return (
    <span key={state}>
      {data.map((item) => (
        <PrEditInput
          key={item.id}
          label={item?.bucket?.name}
          name={item.bucketId}
          defaultValue={item.price}
          onChange={handleBucketsUpdate}
          mood={mood}
        />
      ))}
    </span>
  );
};

export default BucketInput;

5 Comments

setting new value to key props of a component will re-render this component
key is the key!
great!, this saved my day, just trying to find out what is the magic going on here
This should be recognized as a design pattern to be honest.
This can be a bad idea. Changing the key will cause react not just to rerender the component, but entirely recreate it including a full unmount/remount which for some complex component trees could be very expensive.
7

You can set a numeric key on the child component and trigger a key change once an action is performed. e.g

state = {
        childKey: 7,
};



<ChildComponent key={this.state.childKey}/>


actionToTriggerReload = () => {
const newKey = this.state.childKey * 89; // this will make sure the key are never the same
this.setState({childKey: newKey})
   }

This will surely re-render the ChildComponent

Comments

6

You should trigger a re-rendering by calling setState() and giving the new props you want to propagate down. If you really want to force an update you can also call forceUpdate().

If you look at the examples on this page, you can see that setState is the method used to update and trigger a re-rendering. The documentation is also stating (ahaha!) that clearly.

In your case I would call forceUpdate.

EDIT: As Jordan mentioned in the comment, it would be better to store items as part of your state. That way you wouldn't have to call forceUpdate but you would really update the state of your component, thus a regular setState with the updated values would work better.

8 Comments

I am here to validate the approach. Docs mention this: >Normally you should try to avoid all uses of forceUpdate() so I stuck with setState. I want children to react to property change. How can saving list of highlighted items in Parent help in this situation? I am coming from Backbone, so, these questions could sound silly to React world.
In your case it seems better to call forceUpdate. You are not modifying your state, but your props. Calling forceUpdate is the way to go.
I was not sure of what you wanted to do but after looking at your code, you don't have any state, everything is prop based. I would do it differently though. I would let each child be in charge of tracking if they have to be highlighted or not.
"You don't have any state" is incorrect. this.props.item[n].highlighted is by definition state, because it's not passed down from a parent component, it's mutated by this component.
The issue is not "the React terminology" (which is unambiguous), the issue is that this data should go in state, not props, because it's state. Here's a good article on props vs. state: github.com/uberVU/react-guide/blob/master/props-vs-state.md
|
4

Set a numeric default 'key' in the child component and to re-render just change key value.

this.state = {
        updatedKey: 1,
};    
triggerReload = () => {
let newKey = Math.floor(Math.random() * 100); // make sure the key are never the same
this.setState({updatedKey: newKey})
   }
<childComponent key={this.state.updatedKey} handlerProp = {this.onClickItemEvent} />

This worked for me to re-render the ChildComponent in reactjs class base

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.