React Redux Tutorial Часть 4. Использование mapStateToProps API connect для создания приложения Todo


Требования.

https://react-redux.js.org/tutorials/connect#connecting-the-components

На этот раз мы будем использовать создателей действий, которые просто отправляют аргументы в paylaod, и
reducers, которые получают значения из полезной нагрузки и изменяют глобальное состояние в соответствии с перемещенными actionTypes.
Они четко разделены.

Нормальный ? компоненты

  • AddTodo отправляет действия ADD_TODO из входа onChange
  • TodoList рисует список todo и использует внутренние фильтры видимости (VisibilityFilters)
  • Todo рисует один объект и отправляет действие, которое переключает статус выполненного или невыполненного объекта onClick
  • ВидимостьФильтры фильтруют по всем, завершенным и незавершенным условиям. Они принимаются в качестве аргументов activeFilter, который отправляет setFilter.

Конструкция выглядит следующим образом


Проектирование хранилища и редуктора Redux

Поскольку сложно говорить о todo, давайте проследим за фактическим кодом.



redux/actionTypes

Создайте папку redux/ в src/ и поместите в нее

Создайте там файл actionTypes.ts.

export const ADD_TODO = "ADD_TODO";
export const TOGGLE_TODO = "TOGGLE_TODO";
export const SET_FILTER = "SET_FILTER";
Войдите в полноэкранный режим Выход из полноэкранного режима

Необходимыми действиями являются

  • Добавление действия
  • Переключение задачи (переключение между выполненной и невыполненной задачей)
  • Установите фильтры для отображения списка дел.

Это три действия.
Поэтому мы определяем эти три параметра как константы в строке.

Это типы действий.


редукс/действия

Затем создайте действия, основанные на только что созданных типах действий

import { ADD_TODO, TOGGLE_TODO, SET_FILTER } from "./actionTypes";
Войдите в полноэкранный режим Выход из полноэкранного режима

Импортирование типов действий
Используйте эти строковые константы в качестве идентификаторов типов каждого действия.


addTodo

let nextTodoId = 0;

export const addTodo = content => ({
  type: ADD_TODO,
  payload: {
    id: ++nextTodoId,
    content
  }
});
Войдите в полноэкранный режим Выход из полноэкранного режима

Инициализируйте следующий ID значением 0.

Возьмите содержание.
Поместите nextTodoId и полученный контент в paylaod
Установите значение nextTodoId равным +1
Установите тип ADD_TODO

Создайте действие под названием addTodo.


toggleTodo

export const toggleTodo = id => ({
  type: TOGGLE_TODO,
  payload: { id }
});
Войдите в полноэкранный режим Выход из полноэкранного режима

Возьмите идентификатор, передайте его в полезную нагрузку и установите
Добавьте TOGGLE_TODO к типу.


setFilter

export const setFilter = filter => ({ type: SET_FILTER, payload: { filter } });
Войдите в полноэкранный режим Выход из полноэкранного режима

Принимает фильтр, передает его в paylaod и устанавливает тип в
Установите тип на SET_FILTER



редукторы/тодос

Что делают редукторы?

https://react-redux.js.org/tutorials/connect#connecting-the-components

В главе Redux Store/Reducers в описании учебника говорится
Эти редукторы работают совместно с действиями ADD_TODO, TOGGLE_TODO и SET_FILTER.

Существует два типа объектов глобального состояния, которые хранятся.

  • byIDs – это список TODO с содержанием
  • allIds – это просто идентификатор списка TODO.

Объект allIds интерпретируется только как идентификатор списка TODO.

import { ADD_TODO, TOGGLE_TODO } from "../actionTypes";
Войдите в полноэкранный режим Выход из полноэкранного режима

Импортируйте константы для добавления задач и переключения задач из actionTypes.

const initialState = {
  allIds: [],
  byIds: {}
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Определите массив всех идентификаторов и отдельных объектов ID в начальном состоянии.
Определите эти пустые.
Отсюда создается и поддерживается глобальное состояние.

export default function(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO: {
//...
    }
    case TOGGLE_TODO: {
//...
    }
    default:
// ...
  }
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Функции этого модуля следующие
ADD_TODO, TOGGLE_TODO и по умолчанию, который действует как else
Определите эти

Действие просто отправляет тип и объект в полезную нагрузку.
Затем редуктор будет обрабатывать содержимое объекта.


случай ADD_TODO

В случае ADD_TODO

    case ADD_TODO: {
      const { id, content } = action.payload;
      return {
        ...state,
        allIds: [...state.allIds, id],
        byIds: {
          ...state.byIds,
          [id]: {
            content,
            completed: false
          }
        }
      };
    }
Войдите в полноэкранный режим Выход из полноэкранного режима

Разверните ID всех прошлых todos в allIds и добавьте текущий к переданному ID.
Добавьте идентификаторы, которые передаются в данный момент.

В byIds разверните todos в byIds.
В качестве ключа используется ID текущего переданного todo.
Добавить объект с переданным содержимым и завершить: false
и добавить объект с

Добавить текущую двойку к состоянию, содержащему текущие byIds и allIds.
Возвращение с возвращением.

Вот что произойдет при вызове ADD_TODO.


случай TOGGLE_TODO

    case TOGGLE_TODO: {
      const { id } = action.payload;
      return {
        ...state,
        byIds: {
          ...state.byIds,
          [id]: {
            ...state.byIds[id],
            completed: !state.byIds[id].completed
          }
        }
      };
    }
Войдите в полноэкранный режим Выход из полноэкранного режима

В случае TOGGLE_TODO

В отличие от случая ADD, где и id, и содержимое берутся из action.payload, в случае TOGGLE_TODO
Из action.payload извлекается только id.

Расширьте текущее состояние и добавьте к нему эти.

Разверните содержимое текущих byIds в byIds и добавьте их к нему.

Расширьте состояние текущего номера ID в byIds с аргументом ID в качестве ключа и добавьте к нему эти номера.
Отменяет True False завершенного аргумента ID.


по умолчанию

Если ни то, ни другое не происходит, и действие выполняется, оно интерпретируется как возвращение глобального состояния как оно есть.



Функция фильтра

  • Добавление Todo
  • Переключите “Выполнено”.

Подобно этим, изменения фильтра также создают редуктор в отдельном файле.
При изменении фильтра также создается редуктор в отдельном файле.

constants.ts

export const VISIBILITY_FILTERS = {
  ALL: "all",
  COMPLETED: "completed",
  INCOMPLETE: "incomplete"
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Определение констант фильтра дисплея. Это не требует объяснений.


reducers/visibilityFilters.js

import { SET_FILTER } from "../actionTypes";
import { VISIBILITY_FILTERS } from "../../constants";
Войдите в полноэкранный режим Выход из полноэкранного режима

Импортируйте константы SET_FILTER из первого созданного actionTypes.
Импортируйте константы VISIBILITY_FILTERS из констант, созданных непосредственно перед этим
Импортируйте объект константы VISIBILITY_FILTERS из констант, созданных непосредственно перед этим.

const initialState = VISIBILITY_FILTERS.ALL;
Войдите в полноэкранный режим Выход из полноэкранного режима

Установите начальное значение сохраняемого глобального состояния на все

const visibilityFilter = (state = initialState, action) => {
  switch (action.type) {
    case SET_FILTER: {
      return action.payload.filter;
    }
    default: {
      return state;
    }
  }
};
Войдите в полноэкранный режим Выход из полноэкранного режима

При вызове SET_FILTER происходит простой процесс, который возвращает значение фильтра, полученное из фильтра в action.payload, как оно есть.
Простой процесс, который возвращает полученное значение фильтра как есть.

export default visibilityFilter;
Войдите в полноэкранный режим Выход из полноэкранного режима

Затем экспортируйте. Это не требует объяснений.

Теперь все редукторы созданы.


редукторы/индекс

import { combineReducers } from "redux";
import visibilityFilter from "./visibilityFilter";
import todos from "./todos";

export default combineReducers({ todos, visibilityFilter });
Войдите в полноэкранный режим Выход из полноэкранного режима

Наконец, используйте combineReducers в reducers/index, чтобы
Наконец, используйте combineReducers в reducers/index для объединения редукторов. Теперь вы готовы к работе с магазином.



redux/selectors.js

Последний большой для редута. Их несколько.
Их несколько, но, похоже, используется только getTodosByVisibilityFilter.

import { VISIBILITY_FILTERS } from "../constants";
Войдите в полноэкранный режим Выход из полноэкранного режима

Импорт констант фильтра

export const getTodosByVisibilityFilter = (store, visibilityFilter) => {
  const allTodos = getTodos(store);
  switch (visibilityFilter) {
    case VISIBILITY_FILTERS.COMPLETED:
      return allTodos.filter(todo => todo.completed);
    case VISIBILITY_FILTERS.INCOMPLETE:
      return allTodos.filter(todo => !todo.completed);
    case VISIBILITY_FILTERS.ALL:
    default:
      return allTodos;
  }
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Но это не так.
Используйте getTodos для импорта констант фильтров

export const getTodos = store =>
  getTodoList(store).map(id => getTodoById(store, id));
Войдите в полноэкранный режим Выход из полноэкранного режима

getTodos использует getTodoList и getTodoById, которая является новой функцией, позволяющей пользователю создать список объектов todo.

export const getTodoList = store =>
  getTodosState(store) ? getTodosState(store).allIds : [];

export const getTodoById = (store, id) =>
  getTodosState(store) ? { ...getTodosState(store).byIds[id], id } : {};
Войдите в полноэкранный режим Выход из полноэкранного режима

getTodoList и getTodoById использовали getTodoState.

export const getTodosState = store => store.todos;
Войдите в полноэкранный режим Выход из полноэкранного режима

В итоге мы использовали весь файл.



компоненты/TodoList

В компоненте рисования TodoList селекторы

import { getTodosByVisibilityFilter } from "../redux/selectors";

const mapStateToProps = state => {
  const { visibilityFilter } = state;
  const todos = getTodosByVisibilityFilter(state, visibilityFilter);
  return { todos };
}
Войдите в полноэкранный режим Выход из полноэкранного режима

Используйте getTodosByVisibilityFilter, используемый для вывода тодосов.

import { connect } from "react-redux";
export default connect(mapStateToProps)(TodoList);
Войдите в полноэкранный режим Выход из полноэкранного режима

Это связано с тем, что он может быть использован TodoList внутри того же файла.
TodoList внутри одного и того же файла.

import Todo from "./Todo";

const TodoList = ({ todos }) => (
  <ul className="todo-list">
    {todos && todos.length
      ? todos.map((todo, index) => {
          return <Todo key={`todo-${todo.id}`} todo={todo} />;
        })
      : "No todos, yay!"}
  </ul>
);
Войдите в полноэкранный режим Выход из полноэкранного режима

Здесь <Todo/> может быть передан внутри
Todo – это данные, а Todo – это компонент для рисования.
Это довольно запутанно.

AddTodo, Todo, VisibilityFilters,

Они также используют connect для соединения данных и действий таким же образом.

import React from "react";
import { connect } from "react-redux";
import cx from "classnames";
import { toggleTodo } from "../redux/actions";

const Todo = ({ todo, toggleTodo }) => (
  <li className="todo-item" onClick={() => toggleTodo(todo.id)}>
    {todo && todo.completed ? "👌" : "👋"}{" "}
    <span
      className={cx(
        "todo-item__text",
        todo && todo.completed && "todo-item__text--completed"
      )}
    >
      {todo.content}
    </span>
  </li>
);

// export default Todo;
export default connect(
  null,
  { toggleTodo }
)(Todo);
Войдите в полноэкранный режим Выход из полноэкранного режима
import React from "react";
import { connect } from "react-redux";
import { addTodo } from "../redux/actions";

class AddTodo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { input: "" };
  }

  updateInput = input => {
    this.setState({ input });
  };

  handleAddTodo = () => {
    this.props.addTodo(this.state.input);
    this.setState({ input: "" });
  };

  render() {
    return (
      <div>
        <input
          onChange={e => this.updateInput(e.target.value)}
          value={this.state.input}
        />
        <button className="add-todo" onClick={this.handleAddTodo}>
          Add Todo
        </button>
      </div>
    );
  }
}

export default connect(
  null,
  { addTodo }
)(AddTodo);
Войдите в полноэкранный режим Выход из полноэкранного режима
import React from "react";
import cx from "classnames";
import { connect } from "react-redux";
import { setFilter } from "../redux/actions";
import { VISIBILITY_FILTERS } from "../constants";

const VisibilityFilters = ({ activeFilter, setFilter }) => {
  return (
    <div className="visibility-filters">
      {Object.keys(VISIBILITY_FILTERS).map(filterKey => {
        const currentFilter = VISIBILITY_FILTERS[filterKey];
        return (
          <span
            key={`visibility-filter-${currentFilter}`}
            className={cx(
              "filter",
              currentFilter === activeFilter && "filter--active"
            )}
            onClick={() => {
              setFilter(currentFilter);
            }}
          >
            {currentFilter}
          </span>
        );
      })}
    </div>
  );
};

const mapStateToProps = state => {
  return { activeFilter: state.visibilityFilter };
};
// export default VisibilityFilters;
export default connect(
  mapStateToProps,
  { setFilter }
)(VisibilityFilters);

Войдите в полноэкранный режим Выход из полноэкранного режима

Резюме.

Чтобы отделить логику Redux в приложении React Redux и соединить ее с помощью connect

Создайте карту одноименных переменных и строк для ADD_TODO, TOGGLE_TODO, SET_FILTER в redux/actionTypes.js

В redux/actions.js тип применяется из actionTypes, а аргумент помещается в объект в paylaod.

redux/reducers/todo.js
Создайте начальное состояние в redux/reducers/visibilityFilters.js

Создайте initialState в redux/reducers/visibilityFilters.js и используйте его в качестве первого состояния, а также используйте переключатели для передачи его каждому action.type.
Создайте кейс, который обновляет состояние на основе значения action.payload, переданного для каждого action.type.

Объедините их в redux/reducers/index.js с помощью combineReducers и выведите в виде модуля.

Импортируйте эти редукторы в redux/store.js, создайтеStore и снова выведите как модуль

Затем магазин связывается с провайдером в src/index.js и выводится как модуль.
Нарисуйте внутри компонент рисования под названием TodoApp.

В Components/TodoApp нарисуйте больше компонентов рисования под названием AddTodo, TodoList и VisibilityFilters

Components/TodoList использует mapToStateProps и connect для получения значений todo из getTodosByVisibilityFilter.

Фильтр getTodosByVisibilityFilter находится в redux/selectors.js
Тодо извлекается из хранилища в соответствии с фильтром видимости.

Components/TodoList затем расширяет todo в Components/Todo и передает его дальше.

В Components/Todo, когда есть todo.completed, рисуется горизонтальная линия или добавляется className для изменения пиктограммы.

Вот как происходит этот процесс.

Оцените статью
Procodings.ru
Добавить комментарий