Djam
Djam

Reputation: 1043

Trigger child re-rendering in React.js

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'));

Upvotes: 82

Views: 151745

Answers (6)

Subhomoy halder
Subhomoy halder

Reputation: 41

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

Upvotes: 3

Aslam Shekh
Aslam Shekh

Reputation: 706

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}/>

Upvotes: 40

Lekens
Lekens

Reputation: 1969

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

Upvotes: 7

Tofazzal haque
Tofazzal haque

Reputation: 569

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;

Upvotes: 35

Jordan Running
Jordan Running

Reputation: 106027

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/

Upvotes: 48

Jeremy D
Jeremy D

Reputation: 4855

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.

Upvotes: 6

Related Questions