Reputation: 1107
import React from 'react';
import { render } from 'react-dom';
const x = [];
const App = props => <div style={styles}>{JSON.stringify(x)}</div>;
render(<App queries={x} />, document.getElementById('root'));
setInterval(x => x.push(Math.random()), 1000, x);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>React App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
Why doesn't the App component rerender when I mutate the object that I passed to it as a prop.
Whenever I close and reopen React Dev Tools, it shows the new props.
What is happening here? Please help me understand.
Thanks!
Upvotes: 3
Views: 8253
Reputation: 66355
The answer in this case is not related to setState. Sure React will automatically re-render when state changes (as long as you didn't do something like use a PureComponent and then mutate the array or object). But you are doing a render from the root and this has nothing to do with react state as your state exists outside of react.
From the react docs on render:
If the React element was previously rendered into container, this will perform an update on it and only mutate the DOM as necessary to reflect the latest React element.
So would need to call render again at the root level for React to reconcile the changes. Also it's better practice to make a new object/array. So in this case you could do x = x.concat(..)
and then render again.
let x = [];
const App = props => <div>{props.queries.toString()}</div>;
const renderApp = (queries) => ReactDOM.render(<App queries={queries} />, document.getElementById('root'));
setInterval(() => {
x = x.concat(Math.random());
renderApp(x);
}, 1000);
Fiddle: https://jsfiddle.net/ferahl/p7zhs55q/1/
Of course the more usual thing is to use local react state inside react components but it's good to have an understanding of this, and this is how you could implement some global state into react if you weren't using something like redux.
Upvotes: 3
Reputation: 16309
Mutating the data that you pass as props does not trigger a re-render of the component. There is no observer that takes notice of your mutation on that object that could trigger it. In case you have data that changes during the lifetime of a component you need to use state:
import React, {Component} from 'react';
import { render } from 'react-dom';
const x = [];
class App extends Component {
state = {
queries: this.props.queries,
};
componentDidMount() {
setInterval(
() => this.setState(state => ({queries: [...state.queries, Math.random()]}))
), 1000);
}
render() {
return <div style={styles}>{JSON.stringify(this.state.queries)}</div>;
}
}
render(<App queries={x} />, document.getElementById('root'));
This code will initialize the component state with the queries passed via props. When the component did mount it will start the interval which will append a new random number to the state. Calling setState()
will trigger a re-render with the new state. Never mutate props.
Upvotes: 2