简体   繁体   中英

Child Component not updating after state changes

I am learning react, and I am making a simple ToDoApp. I set some todo data from a JSON file in the state of my App Component and use the values to populate a Child component. I wrote a method to be called each time the onChange event is fired on a checkbox element and flip the checkbox by updating the state. Thing is this code worked perfectly fine before, but it's not anymore. The state gets updated accordingly when I change the checkbox, but it doesn't update in the child element, I'd like to know why. Here's my code

App.js

import React from "react";
import TodoItem from "./TodoItem";
import toDoData from "./toDosData";

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            toDoData: toDoData
        };
        this.handleOnChange = this.handleOnChange.bind(this);
    }

    handleOnChange(key)
    {
        this.setState(prevState => {
            let newState = prevState.toDoData.map(currentData => {
                if(currentData.id === key)
                    currentData.completed = !currentData.completed;
                return currentData;
            });
            return {toDoData: newState};
        });
    }

    render() {
        let toDoComponents = this.state.toDoData.map(toDoDatum =>
            <TodoItem key={toDoDatum.id} details={{
                key: toDoDatum.id,
                text: toDoDatum.text,
                completed: toDoDatum.completed,
                onChange: this.handleOnChange
            }} />);
        return (
            <div>
                {toDoComponents}
            </div>
        );
    }
}

export default App;

TodoItem.js

import React from "react";

class TodoItem extends React.Component {

    properties = this.props.details;

    render() {
        return (
            <div>
                <input type="checkbox" checked={this.properties.completed}
                    onChange={() => this.properties.onChange(this.properties.key)}
                />
                <span>{this.properties.text}</span>
            </div>
        )
    }
}

export default TodoItem;

Thanks in advance.

Why do you need to assign your details prop to properties in your class? If you do that properties does not reflect the prop changes and your child component can't see the updates. Just use the props as it is:

render() {
  const { details } = this.props;
  return (
    <div>
      <input
        type="checkbox"
        checked={details.completed}
        onChange={() => details.onChange(details.key)}
      />
      <span>{details.text}</span>
    </div>
  );
}
}

Also, since you don't use any state or lifecycle method in TodoItem component, it can be a functional component as well.

const TodoItem = ({ details }) => (
  <div>
    <input
      type="checkbox"
      checked={details.completed}
      onChange={() => details.onChange(details.key)}
    />
    <span>{details.text}</span>
  </div>
);

One more thing, why don't you pass the todo itself to TodoItem directly?

<TodoItem
  key={toDoDatum.id}
  todo={toDoDatum}
  onChange={this.handleOnChange}
/>

and

const TodoItem = ({ todo, onChange }) => (
  <div>
    <input
      type="checkbox"
      checked={todo.completed}
      onChange={() => onChange(todo.id)}
    />
    <span>{todo.text}</span>
  </div>
);

Isn't this more readable?

Update after comment

 const toDoData = [ { id: 1, text: "foo", completed: false }, { id: 2, text: "bar", completed: false }, { id: 3, text: "baz", completed: false } ]; const TodoItem = ({ todo, onChange }) => ( <div> <input type="checkbox" checked={todo.completed} onChange={() => onChange(todo.id)} /> <span>{todo.text}</span> </div> ); class App extends React.Component { constructor() { super(); this.state = { toDoData: toDoData }; this.handleOnChange = this.handleOnChange.bind(this); } handleOnChange(key) { this.setState(prevState => { let newState = prevState.toDoData.map(currentData => { if (currentData.id === key) currentData.completed = !currentData.completed; return currentData; }); return { toDoData: newState }; }); } render() { let toDoComponents = this.state.toDoData.map(toDoDatum => ( <TodoItem key={toDoDatum.id} todo={toDoDatum} onChange={this.handleOnChange} /> )); return <div>{toDoComponents}</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" /> 

I suggest you this article to understand and avoid the major pitfalls of setState. Once you grok it everything will be clear in your head.

How to become a pro with React setState() in 10 minutes

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