状态更改后子组件不会更新

时间:2019-06-13 22:25:06

标签: javascript reactjs

我正在学习反应,并且正在制作一个简单的ToDoApp。我在应用程序组件的状态下从JSON文件设置了一些待办事项数据,并使用这些值填充子组件。我编写了一个方法,每次在复选框元素上触发onChange事件时都会调用该方法,并通过更新状态来翻转复选框。这段代码以前工作得很好,但是现在已经不行了。当我更改复选框时,状态会相应更新,但在子元素中不会更新,我想知道为什么。这是我的代码

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;

谢谢。

2 个答案:

答案 0 :(得分:2)

为什么您需要在课堂上将details道具分配给properties?如果这样做,properties不会反映道具更改,并且您的子组件也看不到更新。只需按原样使用道具即可:

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

此外,由于在TodoItem组件中不使用任何状态或生命周期方法,因此它也可以是功能组件。

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

还有一件事,为什么不将待办事项直接传递给TodoItem

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

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

这不是更具可读性吗?

评论后更新

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" />

答案 1 :(得分:1)

我建议您本文理解和避免setState的主要陷阱。一旦勾勒它,一切都将在您的脑海中荡漾。

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