微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

类型错误:unshift 不是函数

如何解决类型错误:unshift 不是函数

我已将 todos arrayobjects 作为 props 传递给 TodoList 组件。我正在尝试向 objectarray 添加新的 objects。我创建了一个名为 addTodo方法,它添加了第一个 todoobject,当我尝试添加第二个待办事项时,它失败并显示错误TypeError: this.state.todos.unshift is not a function

两个问题:

  • 为什么它在第二次尝试时失败并在第一次尝试时有效?
  • 我该如何解决

这是我的代码

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

class TodoList extends Component {
  constructor(props){
    super(props);
    
    this.state = { 
      todos: this.props.todos,value: ''
    }

    this.handleChange = this.handleChange.bind(this);
  }

  addTodo(e) {
    e.preventDefault();
    this.setState({
      todos: this.state.todos.unshift({
        id: this.state.todos.length + 1,title: this.state.value,completed: true
      }),value: ''
    });

  }

  handleChange(e) {
    this.setState({ value: e.target.value });
  }

  render() {
    
    return (
      <div>
        <h1>Todos:</h1>
        <form onSubmit={(e) => this.addTodo(e)}>
          <input type='text' value={this.state.value} onChange={this.handleChange} />
        </form>
     
        <ul className='todo-list'>
          { 
            this.props.todos.map(
              todo => 
              <React.Fragment key={todo.id}>
                <li className={`todo-item ${todo.completed ? 'todo-item-completed' : ''}`}>{ todo.title }</li>
                <input type='checkBox' checked={todo.completed} />
              </React.Fragment>
            ) 
          }
        </ul>
      </div>
    )
  }
}

export default TodoList;

编辑:

我根据下面的解释答案重构了我的代码,改为使用 concat() 方法。我摆脱了错误,但它没有将 object 添加array。这是我的新代码

addTodo(e) {
    e.preventDefault();
    
    const newItem = {
      id: this.state.todos.length + 1,completed: true
    };

    this.setState((prevstate) => {
      prevstate.todos.concat(newItem)
    })
    
    console.log(this.state.todos)
  }```

解决方法

Array#unshift 是一个就地函数,它改变它被调用的数组。 unshift 返回更新后数组的长度。添加项目后失败的原因是因为您已经用数字替换了应该是数组的内容。 Number.map 不是一个东西,所以程序崩溃了。

React 的黄金法则是 "never mutate state",因为它混淆了 React 的差异算法,因为它试图找出要重新渲染的内容,结果不可预测。如果您需要使用诸如 unshift 之类的变异函数,请制作一份副本。

在此处尝试使用 Array#concat 而不是 unshiftconcat 是非变异的,并返回调用它的数组的副本,因此 React 不会出现问题,并且直接在传递给 setState 的对象中使用内联是安全的。

第二个问题是reading state values inside of the setState call,比如this.state.todos.length。使用接受先前状态作为参数的 setState 回调版本:

this.setState(prevState => ({
  todos: [{
    id: prevState.todos.length + 1,title: prevState.value,completed: true
  }].concat(prevState.todos)
}));

Spread syntax 在这里代替 concat。下面的代码还修复了使用 this.props.map 而不是 this.state.map 并且不使用 <input> 作为 <ul> 的子元素的错误。

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      todos: this.props.todos,value: ""
    };
    this.handleChange = this.handleChange.bind(this);
  }

  addTodo(e) {
    e.preventDefault();
    this.setState(prevState => ({
      todos: [{
        id: prevState.todos.length + 1,completed: true
      },...prevState.todos]
    }));
  }

  handleChange(e) {
    this.setState({value: e.target.value});
  }

  render() {    
    return (
      <div>
        <h1>Todos:</h1>
        <form onSubmit={(e) => this.addTodo(e)}>
          <input type='text' 
                 value={this.state.value} 
                 onChange={this.handleChange} />
        </form>
        <ul className='todo-list'>
          { 
            this.state.todos.map(todo =>              
              <React.Fragment key={todo.id}>
                <li className={
                  `todo-item ${todo.completed 
                   ? 'todo-item-completed' 
                   : ''}`
                }>
                  { todo.title }
                  <input type='checkbox' checked={todo.completed} />
                </li>
              </React.Fragment>
            ) 
          }
        </ul>
      </div>
    )
  }
}

ReactDOM.render(<TodoList todos={[]} />,document.body);
<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>

使用 function componentsstate hook 将状态片段移动到不同的变量中,从而在大多数用例中简化状态管理。

附带说明,如果您引入删除待办事项的选项,prevState.todos.length + 1 可能不是生成 ID 的糟糕方法。我可以添加一个todo并使长度为1,添加第二个todo使长度为2。然后,如果我删除id为1的第一个项目并创建一个新项目,我有两个id设置为2的项目。单调增加数字或 UUID 可能会更好。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。