Reputation: 1043
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
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
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
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
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
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
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