Как в реакте изменить элемент массива в state

343
15 августа 2021, 02:00

Пытаюсь изменить элемент массива по методу handleToggle, но возникает ошибка, что я делаю не так, и почему в реакте я все время должен возвращать новый массив?

Дочерний компонент TodoList:

function Todolist(props) {
  const todoItem = props.todos.map((todo, index) =>
    <ListItem key={todo.id} dense button>
      <ListItemIcon>
        <Checkbox checked={todo.completed} onChange={props.onChange(todo.id)} edge="start"/>
      </ListItemIcon>
      <ListItemText primary={todo.title} />
      <ListItemSecondaryAction>
        <IconButton edge="end" aria-label="comments"></IconButton>
      </ListItemSecondaryAction>
    </ListItem>
  )
  return (
    <div>
      {todoItem}
    </div>
  )
}

Дочерний компонент FormBox:

function FormBox(props) {
  return (
    <form onSubmit={props.onSubmit}>
      <TextField type="text" variant="filled" className="TextField" fullWidth label="Enter new task" value={props.value} onChange={props.onChange} />
      <Button type="submit" size="large" variant="contained">Add</Button>
    </form>
  )
}

Родительский:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '',
      todos: [
        {
          title: 'Learn React',
          id: Math.random(),
          completed: true
        }
      ]
    }
  };
  handleChange = (evt) => {
    this.setState({
      value: evt.target.value
    })
  };
  handleSubmit = (evt) => {
    evt.preventDefault();
    const todos = [...this.state.todos];
    todos.push({
      title: this.state.value,
      id: Math.random(),
      completed: false
    });
    this.setState(state => ({
      todos,
      value: ''
    }))
  };
  handleToggle = (id) => {
    const todos = [...this.state.todos];
    todos.map(todo => {
      if (todo.id === id) {
        return todo.completed = !todo.completed
      } else {
        return todo
      }
    });
    this.setState(state => ({
      todos
    }))
  };
  render() {
    return (
      <div className="App">
        <Grid className="Header" justify="center" container>
          <Grid item xs={11}>
            <h1 className="Title">My to-do react app</h1>
            <FormBox value={this.state.value} onSubmit={this.handleSubmit} onChange={this.handleChange}/>
          </Grid>
        </Grid>
        <TodoList todos={this.state.todos} onChange={this.handleToggle} />
      </div>
    );
  }
}

Текст ошибки:

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

Answer 1

документация React предупреждает, что состояние не должно быть напрямую изменено и что все должно проходить через setState. А на счет неработающего переключателя, вы возвращаете значение свойства элемента return todo.completed = !todo.completed, т.е. true или false, а должны возвращать элемент с измененным свойством

handleSubmit = (evt) => { 
    evt.preventDefault(); 
     
    this.setState(state => { 
      return { 
        todos: [...state.todos, {title: state.value, id: Math.random(), completed: false}], 
        value: '' 
      } 
    }) 
}; 
 
handleToggle = (id) => { 
  this.setState(state => { 
    return { 
      todos: state.todos.map(todo => todo.id === id ? {...todo, complete: !todo.complete} : todo) 
    } 
  }) 
}

Answer 2

В общем решение было в этом onChange={() => props.onChange(todo.id)}

READ ALSO
Error: TypeScript emitted no output for /frontend/src/index.tsx

Error: TypeScript emitted no output for /frontend/src/index.tsx

При компиляции webpack получаю такую ошибку:

472
При конвертации canvas в DataURL выдаёт белое изображение

При конвертации canvas в DataURL выдаёт белое изображение

Проблема, собственно, описана в заголовке) Ниже мой кодБуду благодарен за любые идеи

114
Скрипт определения наличия блока

Скрипт определения наличия блока

Нужен скрипт, который будет установлен на странице indexhtml и будет на странице /$user_id проверять, есть ли там <div id="reverbs5", если да - то применять...

97