简体   繁体   中英

Component Doesn't Update once the Parent component's state is updated

import React, {Component} from 'react';
import "./DisplayCard.css";

class DisplayCard extends Component {
runArray = (array) => {
  for (var i = 0; i<array.length; i++) {
    return <div>{array[i].task}</div>
  }
}

renderElements = (savedTasks) =>{
  if (savedTasks.length === 0) {
    return <div className="noTasks"> <p>You have no saved tasks.</p> </div>
  } else {
    return this.runArray(savedTasks)
  }
}


render() {
  return (
  <div className="DisplayCardContainer">
    {this.renderElements(this.props.saved)}
  </div>
  )
}
}
export default DisplayCard;

Hey guys,

I am new to react, so this is my child component that takes state from its parent component. My goal is to re-render component every time the array this.props.saved is changed.

This component renders: <p>You have no saved tasks.</p> when the this.props.saved.length === 0 and it renders <div>{array[0].task}</div> when i enter the first task, but it keeps it at <div>{array[0].task}</div> after that. I do see that the state keeps changing and this.props.saved keeps getting bigger, but my component doesn't change anymore.

Here's your problem:

runArray = (array) => {
   for (var i = 0; i<array.length; i++) {
     //the first time we get here, it immediately ends the function!
     return <div>{array[i].task}</div>
   }
}

This loop only ever goes through once (at i=0 ) and then returns, exiting the runArray function and cancelling the rest of the loop. You probably wanted to return an array of elements, one for each of the tasks. I recommend using Array.map() for this, which takes an array and transforms each element, creating a new array:

runArray = (array) => {
    return array.map(arrayElement => <div>arrayElement.task</div>);
}

This should do the trick. Note that React may complain about the fact that your elements lack the key property - see the documentation for more info: https://reactjs.org/docs/lists-and-keys.html

The problem is in your runArray function. Inside your loop, you are returning the first element and that's it. My guess is, you see only the first entry?

When you are trying to render all your tasks, I would suggest to map your tasks, eg

runArray = (array) => array.map(entry => <div>{entry.task}</div>)

It is because you write wrong the runArray function. You make a return in the for loop so it breaks after the first iteration. It will not iterate over the full array.

You need to transform your for loop to a map : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

runArray = (array) => {
  return array.map(v => <div>{v.task}</div>)
}

Does it fix your issue ?

You have to update state of the component to trigger render function. Your render function is not triggered because you did not update the state when the props changed. There are many ways to update state when props updated. One method may be the following:

componentWillReceiveProps(nextProps){
  if (nextProps.saved !== this.props.saved) {
    this.setState({ saved: nextProps.saved })
  }
}

Also change yoour render function to use state of the component as below:

renderElements = () =>{
  if (this.state.savedTasks.length === 0) {
    return <div className="noTasks"> <p>You have no saved tasks.</p> </div>
  } else {
    return this.runArray(this.state.savedTasks)
  }
}

Use .map so that it renders your task correctly. You can remove runArray and rely entirely on props so you don't need to pass arguments across functions as it can get messy quickly. Here's a quick running example of how to create a parent component where you can add a task and pass them into a component so that it renders your data when props are changed, therefore making it reactive.

 class App extends React.Component { state = { taskLabel: "", tasks: [ { id: 1, label: "Do something" }, { id: 2, label: "Learn sometihng" } ] }; handleInput = evt => { this.setState({ [evt.target.name]: evt.target.value }); }; handleSubmit = evt => { evt.preventDefault(); this.setState(prevState => ({ taskLabel: "", tasks: [ ...prevState.tasks, { id: prevState.tasks.length + 1, label: this.state.taskLabel } ] })); }; render() { return ( <div> <form onSubmit={this.handleSubmit}> <input name="taskLabel" type="text" placeholder="Task label" value={this.state.taskLabel} onChange={this.handleInput} /> <button>Create task</button> </form> <DisplayCard tasks={this.state.tasks} /> </div> ); } } class DisplayCard extends React.Component { renderTasks = () => { if (this.props.tasks.length !== 0) { return this.props.tasks.map(task => ( <div key={task.id}>{task.label}</div> )); } else { return <div>No tasks</div>; } }; render() { return <div>{this.renderTasks()}</div>; } } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM