arning:setState(...):在现有状态转换期间无法更新(例如在`render`或其他组件的构造函数中)

时间:2017-02-06 23:29:36

标签: reactjs

var uuid = require('node-uuid');
    import React, { Component } from 'react';
    import './App.css';
    import TodoList from './components/Todolist';
    import AddTodo from './components/AddTodo';
    import SearchTodo  from './components/SearchTodo';

    class App extends Component {
     constructor(props){
      super(props);
      this.state = {
        todos:[{
          id:uuid(),
          text: 'learn React',
          completed:false
        },
        {
          id:uuid(),
          text: 'Cut your hair',
          completed:false
        },
        {
          id: uuid(),
          text: 'go to church',
          completed: true
        }],
        searchVal: '',
        showCompleted: false
      }
      this.handleAddTodo = this.handleAddTodo.bind(this)
      this.handleSearch = this.handleSearch.bind(this)
      this.toggleTodo = this.toggleTodo.bind(this)
      this.filterTodos = this.filterTodos.bind(this)
    }
      handleAddTodo(inputValue){
        this.setState({
          todos:[
            ...this.state.todos,
            {
              id: uuid(),
              text: inputValue,
              completed: false
            }
          ]
        })
      }
      toggleTodo(id){
        var updatedTodo = this.state.todos.map((todo) => {
          if(todo.id === id){
            todo.completed = !todo.completed;
          }
          return todo
        });
        console.log(updatedTodo.completed)
        this.setState({
          todos: updatedTodo,
        })
      }
      handleSearch(searchVal, showCompleted){
        this.setState({
          searchVal: searchVal.toLowerCase(),
          showCompleted: showCompleted
        })
      }

      // Error here
      filterTodos(todos, showCompleted, searchVal){
        var filteredTodos = this.state.todos;
        filteredTodos = filteredTodos.filter((todo) => {
          var text = todo.text.toLowerCase();
          return this.state.searchVal.length === 0 || todo.indexOF(searchVal) > -1
        })
        this.setState({
          todos: filteredTodos
        })
      }

      render() {
        return (
          <div>
          <SearchTodo onSearch={this.handleSearch}/>
           <TodoList todos={this.state.todos} onToggle={this.toggleTodo} filterTodos={this.filterTodos}/>
           <AddTodo handleAdd={this.handleAddTodo}/>
           </div>
        );
      }
    }

    export default App;
  

我正在尝试一个todo列表应用程序。当我尝试编写一个方法来过滤我创建的列表时,我遇到了上面提到的错误,我想知道我做错了什么。

     

否则,有什么更好的方法呢?

1 个答案:

答案 0 :(得分:0)

你的问题列在这一行:

var filteredTodos = this.state.todos;

由于this.state.todos是一个数组,因此您要为filteredTodos分配相同的引用。

这将使状态在下一个代码块中变异,无意中导致React安排更新。您的代码相当于:

this.state.todos = filteredTodos.filter((todo) => {
   var text = todo.text.toLowerCase();
   return this.state.searchVal.length === 0 || todo.indexOF(searchVal) > -1
})

因此,当您在下一行设置State时,由于损坏的更新正在进行,因此React会对您发出警告。

 this.setState({
   todos: filteredTodos
 })

要解决此问题,您应该在过滤之前创建状态的浅表副本。请记住从不改变组件中的状态,应始终将其视为只读值和不可变。在处理数组和对象时创建副本。 Arrays本身的Filter方法总是返回一个新数组:

filterTodos(todos, showCompleted, searchVal){
    var filteredTodos = this.state.todos.filter((todo) => {
      var text = todo.text.toLowerCase();
      return this.state.searchVal.length === 0 || todo.indexOf(searchVal) > -1
    })
    this.setState({
      todos: filteredTodos
    })
  }