Редюсер изменил состояние, но не перерисовывает

Вот мой редуктор:

export interface RootState {
  todos: ToDo[];
}

const initialState = {
  todos: []
};

export const todo = (
  state: RootState = initialState,
  action: Action
): RootState => {
  switch (action.type) {
    case TODO_ADD:
      return {
        todos: [...state.todos, action.payload.todo]
      };
    case TODO_TOGGLE_COMPLETE:
      let todoArr = [...state.todos];
      todoArr.forEach((todo: ToDo, index: number) => {
        if (todo.id === action.payload.id) {
          todoArr[index].isComplete = !todo.isComplete;
        }
      });
      return {
        todos: todoArr
      };
    default:
      return state;
  }
};

действие:

export const TODO_ADD = "TODO_ADD";
export const TODO_TOGGLE_COMPLETE = "TODO_TOGGLE_COMPLETE";

export interface Action {
  type: string;
  payload: any;
}

export interface ToDo {
  id: string;
  todo: string;
  isComplete: boolean;
}

const add = (todo: ToDo): Action => ({
  type: TODO_ADD,
  payload: { todo }
});

export const toggleComplete = (id: string): Action => ({
  type: TODO_TOGGLE_COMPLETE,
  payload: { id }
});

export const addToDo = (todo: ToDo) => (dispatch: any) => {
  dispatch(add(todo));
};

и мой компонент для вызова полного переключения:

import * as React from "react";
import { Button, Row, Col } from "antd";
import styled from "styled-components";
import { ToDo, toggleComplete } from "src/actions/todo";
import { connect } from "react-redux";

const mapDispatchToProps = (dispatch: any) => ({
  toggleComplete: (id: string) => dispatch(toggleComplete(id))
});

type MapDispatchToProps = ReturnType<typeof mapDispatchToProps>;

type Props = MapDispatchToProps & {
  todo: ToDo;
};

class TodoItem extends React.Component<Props> {
  render() {
    const { todo } = this.props;
    return (
      <Container>
        <Col span = {20}>
          <h3>{todo.todo}</h3>
        </Col>
        <Col span = {4}>
          <Button
            type = "primary"
            onClick = {() => this.props.toggleComplete(this.props.todo.id)}
          >
            {todo.isComplete ? "Completed" : "Complete"}
          </Button>
        </Col>
      </Container>
    );
  }
}

const Container = styled(Row)`
  width: 100%;
`;

export default connect<undefined, MapDispatchToProps>(
  undefined,
  mapDispatchToProps
)(TodoItem);

todo получается из списка компонентов

import * as React from "react";
import { List } from "antd";
import styled from "styled-components";
import TodoItem from "./TodoItem";
import { connect } from "react-redux";
import { RootState } from "src/reducers/todo";
import { ToDo } from "src/actions/todo";

const mapStateToProps = (state: RootState) => ({
  todos: state.todos
});

type StateProps = ReturnType<typeof mapStateToProps>;

class TodoList extends React.Component<StateProps> {
  render() {
    const { todos } = this.props;
    return (
      <Container>
        <List
          header = {
            <div>
              <h2>Todo List</h2>
              <h4>
                There are {todos.length} {todos.length === 1 ? "todo" : "todos"}
              </h4>
            </div>
          }
          bordered
          dataSource = {todos}
          renderItem = {(todo: ToDo) => (
            <List.Item>
              <TodoItem todo = {todo} />
            </List.Item>
          )}
        />
      </Container>
    );
  }
}

const Container = styled.div`
  width: 100%;
  margin-top: 30px;
`;

export default connect(mapStateToProps)(TodoList);

Я уже реализовал подписку здесь:

store.subscribe(() => console.info(store.getState()));

и видите, что состояние изменилось, но компонент не перерисовывается.

вы не передаете mapStateToProps в своем вызове подключения. измените undefined на функцию и передайте данные хранилища вашему компоненту... например, что-то вроде const mapStateToProps = (state) => ({ ...state.todos })

John Ruddell 06.06.2019 21:00

в качестве побочного примечания вам на самом деле не нужно mapDispatch, подключение передает диспетчеризацию компоненту. Вы можете просто отправить прямо оттуда. Таким образом, вам не нужно добавлять функцию на карту отправки каждый раз, когда вы хотите добавить ее :)

John Ruddell 06.06.2019 21:03

@JohnRuddell, старший, проверьте мое обновление, от ToDoItem я получаю todo из списка компонентов снаружи

Khuong 06.06.2019 21:04

хорошо, вы где-то комбинируете редукторы? Вы проверили значения реквизита в компоненте TodoList, где вы сопоставляете состояние с реквизитом?

John Ruddell 06.06.2019 21:06

Только один редуктор, не комбинированный. И сопоставление TodoList правильное, я добавил элемент, и он показал этот элемент, но когда я меняю поле isComplete, он не работает.

Khuong 06.06.2019 21:07

@JohnRuddell, если вам нужен весь проект, я могу отправить вам :)

Khuong 06.06.2019 21:12

Это было бы полезно, ха-ха, я собирался настроить скрипку и много копипастить, лол

John Ruddell 06.06.2019 21:13

напишите свой адрес электронной почты или что-то здесь, я отправлю его вам

Khuong 06.06.2019 21:13

отправил, проверьте :)

Khuong 06.06.2019 21:16

Исправлено, дайте мне знать, если у вас есть еще проблемы :)

John Ruddell 06.06.2019 21:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
10
683
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, как вы изменяете состояние в своем редукторе. Измените строку 24 в вашем файле редуктора с этого

todoArr[index].isComplete = !todo.isComplete;

к этому

todoArr[index] = {...todo, isComplete: !todo.isComplete};

по сути, то, что вы пытались сделать, это напрямую изменить объект состояния вместо создания новой подписи объекта.

Еще одна проблема, с которой вы столкнетесь, заключается в том, что в настоящее время вы создаете новые задачи с тем же идентификатором. Таким образом, все элементы помечаются как завершенные, когда вы завершаете один. Вместо этого вы можете просто использовать текущую метку времени в качестве уникального идентификатора.

Изменить это

const todo: ToDo = {
  id: "1",
  todo: value,
  isComplete: false
};
this.props.addToDo(todo);

к этому

const todo: ToDo = {
  id: `${new Date().valueOf()}`,
  todo: value,
  isComplete: false
};
this.props.addToDo(todo);

Это то, что я искал. Спасибо @Джон Радделл :)

Khuong 07.06.2019 04:15

@Khuong в любое время! Дай мне знать, если тебе еще понадобится помощь :)

John Ruddell 07.06.2019 04:18

Другие вопросы по теме