I have been trying to learn React lately and this issue has really confused me. Here's the code:
I have created two class components: App
, representing the parent and Child
, representing the child.
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
randomize() {
this.setState({value: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
The App component contains a button which increments its state value by 1 every time it is clicked. The Child component contains a button which changes its state value to a random number between 0 and 100 every time it is clicked.
While running this, I expected the Child to be re-rendered every time myState
in the parent changed, since that is what I passed down to it as props. I also expected this.state.value
in the Child to change because I set it to be myState
. However, when I incremented myState
, the Child is completely unaffected and still displays whatever randomize()
gave to it before.
Can anyone show me what's wrong? Thanks :)
CodePudding user response:
You have the following snippet in your Child
constructor:
this.state = {
value: this.props.value
}
The above only sets the Child
state once, before the component is mounted. Therefore, none of your increments/decrements push through from your parent to child.
The best solution to your problem is to simply keep the state in your parent component, and only pass down a function that can update it.
Try this:
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
this.updateState = this.updateState.bind(this)
}
updateState(newState) {
this.setState(newState);
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState} update={this.updateState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.randomize = this.randomize.bind(this)
}
randomize() {
this.props.update({myState: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.props.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
CodePudding user response:
You are missing component lifecycle
. constructor
of a class called once only, not on every re-render.
If u wants to change the data of child wrt parent, then use componentDidUpdate()
, which will get called whenever data is updated.
refer here
Modified Code for child:
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
this.setState({value: this.props.value})
}
}
randomize() {
this.setState({value: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}