Реагируйте демонстрацию два - модернизация тодолиста

React.js

create by jsliang on 2019-3-26 09:26:53
Recently revised in 2019-4-7 03:04:01

Привет друзья, если вы считаете, что эта статья неплохая, не забудьте датьstar, друзья моиstarЭто моя мотивация продолжать обновлять!Адрес GitHub


【2019-08-16】Привет друзья, потому чтоjsliangБиблиотека документации подверглась рефакторингу, некоторые ссылки в этой статье могут быть неработоспособны, иjsliangИзвините за отсутствие сил поддерживать старые статьи на стороне Наггетс. Для тех, кому нужно получать последние статьи, щелкните адрес GitHub выше и перейдите в библиотеку документов, чтобы просмотреть скорректированные статьи.


каталог

Чем отличается передок без закидывания от соленой рыбы?

содержание
каталог
2 Предисловие
Три проекта инициализации
Четыре с помощью Ant Design
Шесть исследований Redux
6.1 Redux-плагины
6.2 Объяснение точки знаний REDUX
Семь исследований Redux
7.1 Ввод исходных данных
7.2 Кнопка отправки данных
7.3 Удаление элемента списка TodoItem
Восьмая оптимизация: извлечение ActionType
Девять оптимизаций: извлечь все действие
Десять оптимизаций: компоненты пользовательского интерфейса и компоненты-контейнеры
Одиннадцать оптимизаций: компоненты без сохранения состояния
Twelve End: вызов Axios, Redux-Base завершена
Thirteen Advanced: промежуточное ПО Redux
Fourteen Advanced: промежуточное ПО Redux-Thunk для управления запросами ajax
Fifteen Advanced: промежуточное ПО Redux-Saga для управления запросами Ajax
Шестнадцать продвинутых: React-Redux
17 Резюме

2 Предисловие

Назад к содержанию

Отказ от ответственности: эта серия статей в основном относится к реальному видео MOOC React и написана в сочетании с личным пониманием:

Эта демонстрация обновляет Redux на основе ReactDemoOne, объясняет промежуточное ПО Redux-Thunk и Redux-Saga и, наконец, использует React-Redux для рефакторинга проекта.

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

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

Обратите внимание, что код в этой статье находится в каталоге TodoListUpgrade и имеет четыре папки, соответствующие:

  • Redux-Thunk — на основе Redux-Baseredux-thunkконфигурация промежуточного программного обеспечения
  • Redux-Saga — на основе Redux-Baseredux-sagaконфигурация промежуточного программного обеспечения
  • React-Redux — быть TodoListreact-reduxпеределывать

Конечный каталог проекта Redux-Base выглядит следующим образом, вы можете создать пустой файл и сохранить его, и вы будете использовать его позже:

Три элемента инициализации

Назад к содержанию

ПолучатьРеагировать серииВ коде каталога Simpify скопируйте его в Redux-Base, и приложение, измененное на TodoList, будет небольшим преобразованием TodoList.

Здесь мы начинаем модифицировать:

  1. src/index.js
Сведения о коде
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));
  1. src/App.js TodoList.js
Сведения о коде
import React, { Component } from 'react';
import './index.css';

class TodoList extends Component {
  render() {
    return (
      <div className="App">
        Hello TodoList!
      </div>
    );
  }
}

export default TodoList;
  1. src/index.css
Сведения о коде
/* 尚未进行编写 */

В этот момент мы запускаем в терминалеnpm run start, отображаемый результат:

Четыре с помощью Ant Design

Назад к содержанию

Давайте начнем использовать Ant Design в проекте TodoList:

  • Установить:npm i antd -S
  • использовать:
  1. src/TodoList.js
Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 1. 引入 antd 的列表
import 'antd/dist/antd.css'; // 2. 引入 antd 的样式

// 3. 定义数据
const data = [
  '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
  '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
  '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
  '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
];

class TodoList extends Component {
  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 4. 使用 Input、Button 组件 */}
        <div className="todo-action">
          <Input placeholder='todo' className="todo-input" />
          <Button type="primary" className="todo-submit">提交</Button>
        </div>
        {/* 5. 使用 List 组件 */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={data}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }
}

export default TodoList;
  1. src/index.css
Сведения о коде
.todo {
  width: 1000px;
  margin: 20px auto 0;
  padding: 30px;
  border: 1px solid #ccc;
  border-radius: 10px;
}
.todo-title {
  text-align: center;
}
.todo-action .todo-input {
  width: 200px;
}
.todo-action .todo-submit {
  margin-left: 10px;
}
.todo-list {
  margin-top: 30px;
}

Здесь шаги, которые мы предпринимаем, чтобы обратиться к Ant Design, примерно таковы:

  1. И введение компонентов ввода, кнопок, списка
  2. Ввел ангдный стиль
  3. определить данные
  4. Используйте компоненты Input, Button
  5. Использование компонента списка

Теперь страница выглядит так:

5. Использование Редукса

Назад к содержанию

Я думаю, что необходимо использовать Redux, чтобы испытать его, прежде чем объяснять Redux:

  • Установите Редукс:npm i redux -S
  • Давайте сначала попробуем использовать Redux:

Создайте каталог store в каталоге src для хранения данных Redux.В этом каталоге есть два файла, reducer.js и index.js.

первый, пишем файл reducer.js, который определяет и обрабатывает данные:

  1. src/store/reducer.js
Сведения о коде
// 1. 我们定义一个数据 defaultState
const defaultState = {
  inputValue: '',
  todoList: [
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

// 2. 我们将数据 defaultState 最终以 state 形式导出去
export default (state = defaultState, action) => {
  return state;
}

потом, мы пишем файл index.js, который используется для создания хранилища данных через метод createStore и экспортируем его в TodoList.js для использования.

  1. src/store/index.js
Сведения о коде
import { createStore } from 'redux'; // 3. 我们引用 redux 这个库中的 createStore
import reducer from './reducer'; // 4. 我们引用 reducer.js 中导出的数据

// 5. 我们通过 redux 提供的方法 reducer 来构建一个数据存储仓库
const store = createStore(reducer);

// 6. 我们将 store 导出去
export default store;

наконец, мы ссылаемся на store/index.js в TodoList.js и используем его в списке, а также распечатываем данные, переданные нам магазином:

  1. src/TodoList.js
Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 引入 antd 的组件
import 'antd/dist/antd.css'; // 引入 antd 的样式
import store from './store'; // 7. 引入 store,你可以理解为 store 提供数据。./store 是 ./store/index.js 的缩写

class TodoList extends Component {

  // 8. 在 constructor 中通过 store.getState() 方法来获取数据,并赋值为 state
  constructor(props) {
    super(props);
    // 9. 我们尝试在 Console 中打印 store.getState()
    console.log(store.getState());
    this.state = store.getState();
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 使用 Input、Button 组件 */}
        <div className="todo-action">
          <Input placeholder='todo' className="todo-input" />
          <Button type="primary" className="todo-submit">提交</Button>
        </div>
        {/* 使用 List 组件 */}
        {/* 10. 将原先的 data 换成 state 中的 todoList */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }
}

export default TodoList;

На данный момент мы смотрим на консоль и страницу Chrome и видим, что она работает:

Таким образом, мы завершили [Query] данных в redux, то как мы [модифицируем] данные в redux, а что redux? Давай пойдем.

Шесть исследований Redux

Назад к содержанию

  • Официальный сайт Редукс:Связь
  • Брошюра Redux на китайском языке:Связь

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

6.1 Redux-плагины

Назад к содержанию

  1. Установка: найдите соответствующий подключаемый модуль Chrome в Интернете или загрузите его с Baidu или черезnpm install --save-dev redux-devtoolsУстановите его инструменты разработчика.
  2. использовать:
    1. Закройте браузер и снова откройте, снова откройте консоль (F12), введите строку Redux, указывающую, что вы еще не установили код.
    2. Перейдите на index.js, чтобы установить код.
    3. Проверьте состояние и обнаружите, что данные есть, а плагин Redux установлен.

src/store/index.js

Сведения о коде
import { createStore } from 'redux';
import reducer from './reducer';

// 如果安装了 Redux 工具,则在这里可以直接使用该工具
const store = createStore(
  reducer,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;

6.2 Объяснение очков знаний Redux

Назад к содержанию

Из-за определения React самого себя: «библиотека JavaScript для создания пользовательских интерфейсов».

Итак, когда мы находимся в реакции, если нуждаются в компонентах братьев коммуникаций, такие как темные круги слева от изображения выше, чтобы отправить данные в верхнюю (первую строку) круга, нам нужно много карманного изгиба, что привело к Связь данных очень неприятно.

Появление Redux должно компенсировать этот неприятный метод связи: установить центральный механизм для облегчения связи между компонентами. Таким образом, в правой части рисунка выше показан метод планирования.

Итак, каков механизм запуска/работы Redux? Анализируем по следующим картинкам:

На приведенной выше диаграмме мы предполагаем:

  • Синий (компонент React): заемщик
  • Желтый (создатели действий): действие по заимствованию книг
  • Апельсин (Магазин): Библиотекарь
  • Фиолетовый (редуктор): система

Его процесс можно понять так: сначала заемщик идет на стойку регистрации (действие заимствования), чтобы подать заявку на получение заимствования у библиотекаря, библиотекарь помогает ему найти информацию о книге в системе, затем заставляет компьютер вернуть информацию и, наконец, сообщает ему идти Где взять/как использовать.

В обмен на обычный случай, то есть: когда компонент (компоненты реагирования) должны вызывать данные, он инициирует запрос на создателя (Action Creators), менеджер уведомлений Создателя (Store), менеджер пошел находить соответствующую информацию (редукторы ) Получите информацию обратно, а затем сказали сборку.

В процессе мы будем использовать некоторые общие/основные API Redux:

  1. createStore: создать магазин
  2. store.dispatch: действие отправки
  3. store.getState: Получить все содержимое данных магазина
  4. store.subscribe: отслеживать изменения в магазине, метод будет выполняться при изменении магазина.

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

Модификация данных Seven Redux

Назад к содержанию

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

7.1 Ввод исходных данных

Назад к содержанию

  1. src/TodoList.js
Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 引入 antd 的组件
import 'antd/dist/antd.css'; // 引入 antd 的样式
import store from './store'; // 引入 store,你可以理解为 store 提供数据。./store 是 ./store/index.js 的缩写

class TodoList extends Component {

  // 在 constructor 中通过 store.getState() 方法来获取数据,并赋值为 state
  constructor(props) {
    super(props);
    // 我们尝试在 Console 中打印 store.getState()
    // console.log(store.getState());
    this.state = store.getState();
    
    // 2. 定义 handleInputChange
    this.handleInputChange = this.handleInputChange.bind(this);

    // 7. 绑定方法 handleStoreChange 来处理 Redux 返回回来的数据
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 使用 Input、Button 组件 */}
        <div className="todo-action">
          {/* 1. Input 绑定 handleInputChange 事件 */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.state.inputValue}
            onChange={this.handleInputChange}
          />
          <Button 
            type="primary" 
            className="todo-submit"
          >
            提交
          </Button>
        </div>
        {/* 使用 List 组件 */}
        {/* 将原先的 data 换成 state 中的 todoList */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }

  // 3. 编写 handleInputChange
  handleInputChange(e) {
    // 4. 通过 action,将数据传递给 store
    const action = {
      type: 'change_input_value',
      value: e.target.value
    }
    store.dispatch(action);
  }

  // 8. 在 handleStoreChange 中处理数据
  handleStoreChange() {
    this.setState(store.getState());
  }

}

export default TodoList;
  1. src/store/reducer.js
Сведения о коде
// 定义一个数据 defaultState
const defaultState = {
  inputValue: '',
  todoList: [
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

// 将数据 defaultState 最终以 state 形式导出去
export default (state = defaultState, action) => {
  // 5. 打印 state 和 action
  console.log(state);
  console.log(action);

  // 6. 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState
  }
  
  return state;
}

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

  1. В компоненте Input мы даем емуonChangeограниченный по времениhandleInputChangeмероприятие.
  2. определениеhandleInputChangeметод.
  3. записыватьhandleInputChangeметод.
  4. мы вhandleInputChangeнаписано наaction,пройти черезdispatchБудуactionToList.js redux передается из редуктора .js.
  5. печать в редукторе.jsstateа такжеaction.
  6. Redux получен в reducer.jsstateа такжеaction, то ставим новыйnewStateВернуться к спине (чтобы вернуться в SRC / Store / index.js, обратно в SRC / ToList.js), вы можете получить желаемый ToList.js обратной связи.
  7. в TodoListconstructorпрошедшийstore.subscribeПривяжите метод обработки для обработки данных, возвращаемых Redux.handleStoreChange.
  8. существуетhandleStoreChangeмы напрямуюsetStateСостояние, возвращаемое Redux, т.е.store.getState().

В настоящее время, если мы посмотрим на точки знаний Redux в главе 6.2, мы обнаружим, что точки знаний гладкие.

Ссылка: Счетчик

Сведения о коде
import { createStore } from 'redux';

/**
 * 这是一个 reducer,形式为 (state, action) => state 的纯函数。
 * 描述了 action 如何把 state 转变成下一个 state。
 *
 * state 的形式取决于你,可以是基本类型、数组、对象、
 * 甚至是 Immutable.js 生成的数据结构。惟一的要点是
 * 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
 *
 * 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
 * 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
 */
function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);

// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
  console.log(store.getState())
);

// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1

7.2 Кнопка отправки данных

Назад к содержанию

Ниже мы приводим событие Enter для ввода и событие отправки с помощью кнопки.Друзья могут обратиться к событию ввода ввода, написать его самостоятельно, а затем просмотреть эту главу после написания, и вы получите больше.

src/TodoList.js

Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 引入 antd 的组件
import 'antd/dist/antd.css'; // 引入 antd 的样式
import store from './store'; // 引入 store,你可以理解为 store 提供数据。./store 是 ./store/index.js 的缩写

class TodoList extends Component {

  // 在 constructor 中通过 store.getState() 方法来获取数据,并赋值为 state
  constructor(props) {
    super(props);
    // 我们尝试在 Console 中打印 store.getState()
    // console.log(store.getState());
    this.state = store.getState();
    
    // 处理 handleInputChange 方法
    this.handleInputChange = this.handleInputChange.bind(this);

    // 绑定方法 handleStoreChange 来处理 Redux 返回回来的数据
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // 2. 处理 handleAddItem 方法
    this.handleAddItem = this.handleAddItem.bind(this);

    // 7. 处理 handleInputKeyUp 方法
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 使用 Input、Button 组件 */}
        <div className="todo-action">
          {/* Input 绑定 handleInputChange 事件 */}
          {/* 6. Input 绑定回车事件:handleInputKeyUp */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.state.inputValue}
            onChange={this.handleInputChange}
            onKeyUp={this.handleInputKeyUp}
          />
          {/* 1. 为 Button 定义点击执行 handleAddItem 方法 */}
          <Button 
            type="primary" 
            className="todo-submit"
            onClick={this.handleAddItem}
          >
            提交
          </Button>
        </div>
        {/* 使用 List 组件 */}
        {/* 将原先的 data 换成 state 中的 todoList */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }

  // 编写 handleInputChange 方法
  handleInputChange(e) {
    // 通过 dispatch(action),将数据传递给 store
    const action = {
      type: 'change_input_value',
      value: e.target.value
    }
    store.dispatch(action);
  }

  // 在 handleStoreChange 中处理数据
  handleStoreChange() {
    this.setState(store.getState());
  }

  // 3. 编写 handleAddItem 方法
  handleAddItem() {
    // 4. 通过 dispatch(action),将数据传递给 store
    const action = {
      type: 'add_todo_item'
    }
    store.dispatch(action);
  }

  // 8. 为 Input 的 keyUp 方法 handleInputKeyUp 绑定 handleAddItem
  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

}

export default TodoList;

src/store/reducer.js

Сведения о коде
// 定义一个数据 defaultState
const defaultState = {
  inputValue: '',
  todoList: [
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

// 将数据 defaultState 最终以 state 形式导出去
export default (state = defaultState, action) => {
  // 打印 state 和 action
  // console.log(state);
  // console.log(action);

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // 5. 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = '';
    return newState;
  }

  return state;
}

В это время все наши события отправки кнопок обрабатываются, и в это время реализуется функция страницы:

Хорошо, давайте снова разберем процесс:

  1. Определить выполнение щелчка для кнопкиhandleAddItemметод
  2. иметь дело сhandleAddItemметод
  3. записыватьhandleAddItemметод
  4. пройти черезdispatch(action), передавая данные вstore
  5. Получите данные в reducer.js и верните их для обработки результата
  6. Ввод связывает событие возврата каретки:handleInputKeyUp
  7. иметь дело сhandleInputKeyUpметод
  8. для метода ввода keyUphandleInputKeyUpсвязыватьhandleAddItem

Стоит отметить, что когда мы находимся в Input, мы сделалиhandleStoreChangeобработка, поэтому не пишемstore.subscribe()Для мониторинга изменений данных, поэтому друзья должны обратить внимание на общий процесс.

7.3 Удаление элемента списка TodoItem

Назад к содержанию

Затем мы добавим событие удаления к щелчку элемента списка.

src/TodoList.js

Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 引入 antd 的组件
import 'antd/dist/antd.css'; // 引入 antd 的样式
import store from './store'; // 引入 store,你可以理解为 store 提供数据。./store 是 ./store/index.js 的缩写

class TodoList extends Component {

  // 在 constructor 中通过 store.getState() 方法来获取数据,并赋值为 state
  constructor(props) {
    super(props);
    // 我们尝试在 Console 中打印 store.getState()
    // console.log(store.getState());
    this.state = store.getState();
    
    // 处理 handleInputChange 方法
    this.handleInputChange = this.handleInputChange.bind(this);

    // 绑定方法 handleStoreChange 来处理 Redux 返回回来的数据
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // 处理 handleAddItem 方法
    this.handleAddItem = this.handleAddItem.bind(this);

    // 处理 handleInputKeyUp 方法
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 使用 Input、Button 组件 */}
        <div className="todo-action">
          {/* Input 绑定 handleInputChange 事件 */}
          {/* Input 绑定回车事件:handleInputKeyUp */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.state.inputValue}
            onChange={this.handleInputChange}
            onKeyUp={this.handleInputKeyUp}
          />
          {/* 为 Button 定义点击执行 handleAddItem 方法 */}
          <Button 
            type="primary" 
            className="todo-submit"
            onClick={this.handleAddItem}
          >
            提交
          </Button>
        </div>
        {/* 使用 List 组件 */}
        {/* 将原先的 data 换成 state 中的 todoList */}
        {/* 1. 列表点击事件绑定 handleDeleteItem 方法 */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (
              <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item>
            )}
          />
        </div>
      </div>
    );
  }

  // 编写 handleInputChange 方法
  handleInputChange(e) {
    // 通过 dispatch(action),将数据传递给 store
    const action = {
      type: 'change_input_value',
      value: e.target.value
    }
    store.dispatch(action);
  }

  // 在 handleStoreChange 中处理数据
  handleStoreChange() {
    this.setState(store.getState());
  }

  // 编写 handleAddItem 方法
  handleAddItem() {
    // 通过 dispatch(action),将数据传递给 store
    const action = {
      type: 'add_todo_item'
    }
    store.dispatch(action);
  }

  // 为 Input 的 keyUp 方法 handleInputKeyUp 绑定 handleAddItem
  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  // 2. 编写 handleDeleteItem 方法
  handleDeleteItem(index) {
    console.log(index);
    // 3. 通过 dispatch(action),将数据传递给 store
    const action = {
      type: 'delete_todo_item',
      index
    }
    store.dispatch(action);
  }

}

export default TodoList;

src/store/reducer.js

Сведения о коде
// 定义一个数据 defaultState
const defaultState = {
  inputValue: '',
  todoList: [
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

// 将数据 defaultState 最终以 state 形式导出去
export default (state = defaultState, action) => {
  // 打印 state 和 action
  // console.log(state);
  // console.log(action);

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'add_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = '';
    return newState;
  }

  // 4. 在 reducer.js 中获取数据,并 return 回去处理结果
  if(action.type === 'delete_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  return state;
}

Теперь давайте сделаем функциональную демонстрацию:

Давайте посмотрим на наши идеи программирования:

  1. Событие щелчка по списку привязано к методу handleDeleteItem. На данный момент, в связи с необходимостью привязкиthisи передать значениеindex, т.е. два значения, поэтому мы прямо в коде:this.handleDeleteItem.bind(this, index)
  2. Напишите метод handleDeleteItem
  3. Передать данные в магазин через диспетчеризацию (действие)
  4. Получите данные в reducer.js и верните их для обработки результата

Таким образом, мы завершили удаление элемента списка.

На данный момент мы знакомы с методами сбора и модификации данных Reudx.

Восьмая оптимизация: извлечь тип в действии

Назад к содержанию

В приведенной выше главе мы завершили построение TodoList, можно сказать, что сделали это.

Но, знаете, эта статья называется: [React Demo Two — TodoList Upgrade]

То есть мы не только обновляемся до Redux, но и продолжаем обновление, чтобы подготовить почву для разработки масштабных проектов.

Поэтому этот раздел начинаем оптимизировать.

В приведенном выше коде мы не нашли насactionизtypeНаписано ли оно в TodoList.js, и с ним сложно разобраться, если их слишком много?

  • change_input_value
  • add_todo_item
  • delete_todo_item

Итак, нам нужно обработать следующий тип, мы добавляем новый actionTypes.js в директорию хранилища:

src/store/actionTypes.js

Сведения о коде
// 1. 定义 actionTypes
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM =  'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';

Затем в TodoList.js и reducer.js используйте:

src/TodoList.js

Сведения о коде
import React, { Component } from 'react'; // 引入 React 及其 Component
import './index.css'; // 引入 index.css
import { Input, Button, List } from 'antd'; // 引入 antd 的组件
import 'antd/dist/antd.css'; // 引入 antd 的样式
import store from './store'; // 引入 store,你可以理解为 store 提供数据。./store 是 ./store/index.js 的缩写
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'; // 2. 引用 actionTypes

class TodoList extends Component {

  // 在 constructor 中通过 store.getState() 方法来获取数据,并赋值为 state
  constructor(props) {
    super(props);
    // 我们尝试在 Console 中打印 store.getState()
    // console.log(store.getState());
    this.state = store.getState();
    
    // 处理 handleInputChange 方法
    this.handleInputChange = this.handleInputChange.bind(this);

    // 绑定方法 handleStoreChange 来处理 Redux 返回回来的数据
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);

    // 处理 handleAddItem 方法
    this.handleAddItem = this.handleAddItem.bind(this);

    // 处理 handleInputKeyUp 方法
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        {/* 使用 Input、Button 组件 */}
        <div className="todo-action">
          {/* Input 绑定 handleInputChange 事件 */}
          {/* Input 绑定回车事件:handleInputKeyUp */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.state.inputValue}
            onChange={this.handleInputChange}
            onKeyUp={this.handleInputKeyUp}
          />
          {/* 为 Button 定义点击执行 handleAddItem 方法 */}
          <Button 
            type="primary" 
            className="todo-submit"
            onClick={this.handleAddItem}
          >
            提交
          </Button>
        </div>
        {/* 使用 List 组件 */}
        {/* 将原先的 data 换成 state 中的 todoList */}
        {/* 列表点击事件绑定 handleDeleteItem 方法 */}
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (
              <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item>
            )}
          />
        </div>
      </div>
    );
  }

  // 编写 handleInputChange 方法
  handleInputChange(e) {
    // 通过 dispatch(action),将数据传递给 store
    // 3. 使用 actionTypes
    const action = {
      type: CHANGE_INPUT_VALUE,
      value: e.target.value
    }
    store.dispatch(action);
  }

  // 在 handleStoreChange 中处理数据
  handleStoreChange() {
    this.setState(store.getState());
  }

  // 编写 handleAddItem 方法
  handleAddItem() {
    // 通过 dispatch(action),将数据传递给 store
    // 3. 使用 actionTypes
    const action = {
      type: ADD_TODO_ITEM
    }
    store.dispatch(action);
  }

  // 为 Input 的 keyUp 方法 handleInputKeyUp 绑定 handleAddItem
  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  // 编写 handleDeleteItem 方法
  handleDeleteItem(index) {
    console.log(index);
    // 通过 dispatch(action),将数据传递给 store
    // 3. 使用 actionTypes
    const action = {
      type: DELETE_TODO_ITEM,
      index
    }
    store.dispatch(action);
  }

}

export default TodoList;

src/store/reducer.js

Сведения о коде
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'; // 2. 引用 actionTypes

// 定义一个数据 defaultState
const defaultState = {
  inputValue: '',
  todoList: [
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    // '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

// 将数据 defaultState 最终以 state 形式导出去
export default (state = defaultState, action) => {
  // 打印 state 和 action
  // console.log(state);
  // console.log(action);

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  // 3. 使用 actionTypes
  if(action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  // 3. 使用 actionTypes
  if(action.type === ADD_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = '';
    return newState;
  }

  // 在 reducer.js 中获取数据,并 return 回去处理结果
  // 3. 使用 actionTypes
  if(action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  return state;
}

Кроме того, смысл извлечения actionTypes.js заключается в том, чтобы исправить значение action.type, чтобы он не сообщал об ошибке, потому что он используется в двух разных местах.

Девять оптимизаций: извлечь все действие

Назад к содержанию

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

После очистки мы можем обнаружить, что хотя actionType извлекается, когда страница достаточно сложна, нашactionЭто все еще очень сложно в управлении, поэтому мы пытаемся поставить всеactionизвлекать.

Мы создаем новый actionCreators.js в каталоге магазина:

src/store/actionCreators.js

Сведения о коде
// 1. 引入 actionTypes
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

// 2. 导出相应 action
export const getInputChangeAction = (value) => ({
  type: CHANGE_INPUT_VALUE,
  value
})

export const getAddItemAction = () => ({
  type: ADD_TODO_ITEM
})

export const getItemDeleteAction = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})

Остроумные друзья, увидев это, вы должны понять наш замысел, поэтому давайте снова модифицируем TodoList.js:

src/TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import store from './store';
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem } from './store/actionCreators'; // 3. 引入 actionCreators

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        <div className="todo-action">
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.state.inputValue}
            onChange={this.handleInputChange}
            onKeyUp={this.handleInputKeyUp}
          />
          <Button 
            type="primary" 
            className="todo-submit"
            onClick={this.handleAddItem}
          >
            提交
          </Button>
        </div>
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.todoList}
            renderItem={(item, index) => (
              <List.Item onClick={this.handleDeleteItem.bind(this, index)}>{index + 1} - {item}</List.Item>
            )}
          />
        </div>
      </div>
    );
  }

  handleInputChange(e) {
    // 4. 使用 actionCreators 中的 getChangeInputValue
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

  handleStoreChange() {
    this.setState(store.getState());
  }

  handleAddItem() {
    // 4. 使用 actionCreators 中的 getAddTodoItem
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    // 4. 使用 actionCreators 中的 getAddTodoItem
    const action = getDeleteTodoItem(index);
    store.dispatch(action);
  }

}

export default TodoList;

Таким образом, мы помещаем весьactionИзвлеченный, в больших проектах, он будет очень удобен для нашей работы.

Десять оптимизаций: компоненты пользовательского интерфейса и компоненты-контейнеры

Назад к содержанию

Теперь сначала бросьте два определения:

  • Компоненты пользовательского интерфейса - обманывайте компоненты, выполняйте рендеринг страницы
  • Компоненты-контейнеры — смарт-компоненты, выполняющие логику страницы

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

Здесь мы разделяем компоненты:

src/TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem } from './store/actionCreators';
// 1. 将 Input 等 antd 的组件引入迁移到 TodoListUI,并引入 TodoListUI
import TodoListUI from './TodoListUI';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      // 2. 编写 TodoListUI,传递参数到 TodoListUI 中
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />
    );
  }

  handleInputChange(e) {
    // 解决 Antd 中的 bug
    e.persist();
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

  handleStoreChange() {
    this.setState(store.getState());
  }

  handleAddItem() {
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    // 解决 Antd 中的 bug
    e.persist();
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    // 解决 Antd 中的 bug
    index.persist();
    const action = getDeleteTodoItem(index);
    store.dispatch(action);
  }

}

export default TodoList;

Здесь мы извлекаем содержимое в рендере в подкомпонент, который находится в каталоге src, называемый TodoListUI, мы используем TodoList.js в качестве компонента-контейнера, просто передаем данные в TodoListUI, а затем пишем содержимое компонента пользовательского интерфейса. :

src/TodoListUI.js

Сведения о коде
// 3. 引入 Input 等组件
import React, { Component } from 'react';
import { Input, Button, List } from 'antd';

class TodoListUI extends Component {
  render() {
    return (
      // 4. 接收 TodoList.js 中传递的数据
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        <div className="todo-action">
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.props.inputValue}
            onChange={this.props.handleInputChange}
            onKeyUp={this.props.handleInputKeyUp}
          />
          <Button 
            type="primary" 
            className="todo-submit"
            onClick={this.props.handleAddItem}
          >
            提交
          </Button>
        </div>
        <div className="todo-list">
        {/* 5. 在处理 handleDeleteItem 的时候需要注意,index 的值需要再进行处理 */}
          <List
            size="large"
            bordered
            dataSource={this.props.todoList}
            renderItem={(item, index) => (
              <List.Item onClick={() => {this.props.handleDeleteItem(index)}}>
                {index + 1} - {item}
              </List.Item>
            )}
          />
        </div>
      </div>
    );
  }
}

export default TodoListUI;

Таким образом, мы завершили разделение компонентов пользовательского интерфейса и компонентов-контейнеров.

Что мы делаем:

  1. Перенесите и другие компоненты, такие как Input, в TodoListUI и внедрите TodoListUI.
  2. Напишите TodoListUI и передайте параметры в TodoListUI
  3. Внедрение таких компонентов, как ввод
  4. Получить данные, переданные в TodoList.js
  5. При обработке handleDeleteItem нужно обратить внимание на то, что значение индекса нужно обрабатывать заново

Таким образом, мы завершили извлечение страниц, когда у нас будет слишком много страниц, мы разделим контент на компоненты пользовательского интерфейса. С другой стороны, компоненты-контейнеры могут содержать бесконечное количество компонентов пользовательского интерфейса. так:

Компонент-контейнер — это умный компонент, который управляет всем; в то время как компонент пользовательского интерфейса — это компонент-дурак, ему нужно только выполнить событие, переданное компонентом-контейнером, и отобразить страницу.

Одиннадцать оптимизаций: компоненты без сохранения состояния

Назад к содержанию

Когда компонент имеет только функцию render() и больше ничего, мы называем его компонентом без состояния.

В проекте TodoList наш TodoListUI работает только с render(), поэтому мы можем использовать TodoListUI как компонент без состояния:

src/TodoListUI

Сведения о коде
// 1. 我们不需要 react 中的 Component 了
import React from 'react';
import { Input, Button, List } from 'antd';

// class TodoListUI extends Component {

// 2. 进行无状态组件定义,然后父组件传递过来的数据,通过 props 获取
const TodoListUI = (props) => {
  // 3. 我们不需要进行 render 了,直接 return 就可以了
  return (
    // 4. 接收 TodoList.js 中传递的数据
    <div className="todo">
      <div className="todo-title">
        <h1>TodoList</h1>
      </div>
      <div className="todo-action">
        {/* 5. 我们修改 this.props 为 props */}
        <Input 
          placeholder='todo' 
          className="todo-input" 
          value={props.inputValue}
          onChange={props.handleInputChange}
          onKeyUp={props.handleInputKeyUp}
        />
        <Button 
          type="primary" 
          className="todo-submit"
          onClick={props.handleAddItem}
        >
          提交
        </Button>
      </div>
      <div className="todo-list">
        <List
          size="large"
          bordered
          dataSource={props.todoList}
          renderItem={(item, index) => (
            <List.Item onClick={() => {props.handleDeleteItem(index)}}>
              {index + 1} - {item}
            </List.Item>
          )}
        />
      </div>
    </div>
  );
}

export default TodoListUI;

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

  1. Нам не нужно реагировать вComponent, поэтому мы удалилиComponent
  2. Определите компонент без состояния, а затем данные, переданные родительским компонентом, будут получены через реквизиты.
  3. Нам больше не нужно рендерить, просто вернитесь напрямую
  4. Получить данные, переданные в TodoList.js
  5. Изменяем this.props на props

Twelve End: вызов Axios, Redux-Base завершена

Назад к содержанию

Наконец дошли до финальной ссылки, нам нужно получить интерфейс предоставляемый бэкендом для дальнейшей разработки.

  • Представляем Аксиос:cnpm i axios -S
  • существуетcomponentDidMountПолучите данные интерфейса из , выполните процесс и, наконец, отобразите его на странице:

TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 7. 从 actionCreators 中引入 initListAction
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, initListAction } from './store/actionCreators';
import TodoListUI from './TodoListUI';
import axios from 'axios'; // 1. 引入 axios

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />
    );
  }

  // 2. 在 componentDidMount() 中进行 axios 接口调用
  componentDidMount() {
    axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolist').then( (res) => {
      console.log(res.data.todolist);
      // 3. 将接口数据 dispatch 到 action 中,所以需要先前往 actionCreators.js 中创建 action
      // 8. 创建 action 并 dispatch 到 reducer.js 中
      const action = initListAction(res.data.todolist);
      store.dispatch(action);
    })
  }

  handleInputChange(e) {
    // 解决 Antd 中的 bug
    e.persist();
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

  handleStoreChange() {
    this.setState(store.getState());
  }

  handleAddItem() {
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    // 解决 Antd 中的 bug
    e.persist();
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    // 解决 Antd 中的 bug
    index.persist();
    const action = getDeleteTodoItem(index);
    store.dispatch(action);
  }

}

export default TodoList;

actionCreators.js

Сведения о коде
// 5. 从 actionTypes 引入 INIT_LIST_ACTION
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';

export const getChangeInputValue = (value) => ({
  type: CHANGE_INPUT_VALUE,
  value
})

export const getAddTodoItem = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteTodoItem = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})

// 4. 编写导出的 initListAction,所以需要先在 actionTypes 中引入 INIT_LIST_ACTION
export const initListAction = (data) => ({
  type: INIT_LIST_ACTION,
  data
})

actionTypes.js

Сведения о коде
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM =  'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
// 6. 导出 INIT_LIST_ACTION
export const INIT_LIST_ACTION = 'init_list_action';

reducer.js

Сведения о коде
// 9. 从 actionTypes 引用 INIT_LIST_ACTION
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';

const defaultState = {
  inputValue: '',
  todoList: []
}

export default (state = defaultState, action) => {

  if(action.type === CHANGE_INPUT_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  if(action.type === ADD_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.push(newState.inputValue);
    newState.inputValue = '';
    return newState;
  }

  if(action.type === DELETE_TODO_ITEM) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList.splice(action.index, 1);
    return newState;
  }

  // 10. 接受 TodoList 传递过来的数据,并进行处理与返回
  if(action.type === INIT_LIST_ACTION) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.todoList = action.data;
    return newState;
  }

  return state;
}

Таким образом, мы завершили вызов axios, отрендерили его на странице и разобрались.Идея такова:

  1. TodoList.js — импортaxios
  2. TodoList.js — в компонентеDidMount()axiosвызов интерфейса
  3. TodoList.js — данные интерфейсаdispatchприбытьaction, поэтому вам нужно сначала перейти к actionCreators.js, чтобы создатьaction
  4. actionCreators.js — написать экспортированныйinitListAction, поэтому его нужно сначала ввести в actionTypesINIT_LIST_ACTION
  5. actionCreators.js — импортировано из actionTypesINIT_LIST_ACTION
  6. actionTypes.js — экспортINIT_LIST_ACTIONк действиюCreators
  7. TodoList.js — импортировано из actionCreatorsinitListAction
  8. ToList.js - Созданиеactionа такжеdispatchв редуктор.js
  9. редуктор.js — ссылка из actionTypesINIT_LIST_ACTION
  10. редуктор.js - принять данные, переданные TodoList, обработать и вернуть

Таким образом, мы завершили вызов интерфейса, и страница выглядит следующим образом:

На данный момент мы завершили Redux-Base.

Однако это просто простое использование redux. Мы можем чувствовать, что просто используя redux по-прежнему сложно для проекта, поэтому нам нужно промежуточное программу Redux-Thunk и Redux-Saga. И, наконец, попробуйте использовать React-redux.

Тринадцать продвинутых: ПО промежуточного слоя Redux

Назад к содержанию

  • Что такое промежуточное программное обеспечение?

Middleware — это подключаемый модуль, организованный между кем и кем.

  • Что такое промежуточное ПО Redux?

Посмотрите на картинку:

Как видно на рисунке выше, когда мы отправляем действие в хранилище через Dispatch, мы обращаемся к промежуточному программному обеспечению в Dispatch для обработки. Он инкапсулирует и обновляет Dispatch, так что мы можем использовать не только объекты в Dispatch, но и функции методов.

Таким образом, когда мы передаем объект в Dispatch, это ничем не отличается от нашего обычного использования избыточности. Однако когда мы передаем функцию в Dispatch, если мы используем Redux-Thunk или Redux-Saga, они обработают это, что позволит нам также вызвать функцию.

Поэтому, говоря простым языком, промежуточное ПО Redux — это пакетное обновление Dispatch.

Fourteen Advanced: промежуточное ПО Redux-Thunk для управления запросами ajax

Назад к содержанию

В главе 12 мы сделали Ajax-запрос в TodoList, и это нормально.

Однако с увеличением количества запросов Ajax, если мы все напишем на странице, страница будет выглядеть раздутой.

На данный момент необходим Redux-Thunk. Redux-Thunk может извлекать асинхронные запросы и сложную бизнес-логику в другие места для обработки.

Копируем копию кода Redux-Base в директорию Redux-Thunk и выполняем:

Примечание. Нет необходимости копировать папку node_modules.

  • Установите зависимости:npm i
  • Запустите проект:npm run start

Затем мы начинаем ссылаться на Redux-Thunk:

  • Редукс Преобразователь:адрес гитхаба
  • Установить:npm i redux-thunk -S
  • Учебный пример:

test.js

Сведения о коде
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
)

Выглядит очень просто, есть лес, попробуем в проекте.

src/store/index.js

Сведения о коде
// 2. 从 redux 中引入 applyMiddleware,applyMiddleware 的作用是应用 redux 中间件
// 3. 引入 compose 函数,因为我们用到了两个中间件:redux-thunk 以及 redux-devtools-extension,需要 compose 辅助
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
// 1. 从 redux-thunk 中引入 thunk
import thunk from 'redux-thunk';

// 3. 使用 redux-devtools-extension 中间件
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

// 4. 使用 applyMiddleware 对此进行扩展
const enhancer = composeEnhancers(
  applyMiddleware(thunk),
);

// 5. 在 createStore 进行 enhancer 调用
const store = createStore(
  reducer,
  enhancer
);

export default store;

Здесь мы делаем несколько вещей:

  1. отredux-thunkвведен вthunk
  2. отreduxвведен вapplyMiddleware,applyMiddlewareРоль заключается в применении нескольких промежуточных программ Redux.
  3. представлятьcomposeфункцию, потому что мы используем два промежуточных ПО:redux-thunkтак же какredux-devtools-extension,нужноcomposeВспомогательный
  4. использоватьredux-devtools-extensionпромежуточное ПО
  5. использоватьapplyMiddlewareРасширение этого, т.е.redux-thunkпромежуточное ПО плюсredux-devtools-extensionпромежуточное ПО
  6. существуетcreateStoreпровестиenhancerпередача

Таким образом, мы используем оба в одном проектеredux-thunkпромежуточное ПО плюсredux-devtools-extensionпромежуточное ПО, которое делаетredux-thunkцитаты.

Далее мы будем использоватьredux-thunkохватывать

src/store/actionCreators.js

Сведения о коде
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION } from './actionTypes';
// 1. 把 axios 从 TodoList.js 中剪切到 actionCreators.js 中
import axios from 'axios';

export const getChangeInputValue = (value) => ({
  type: CHANGE_INPUT_VALUE,
  value
})

export const getAddTodoItem = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteTodoItem = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})

export const initListAction = (data) => ({
  type: INIT_LIST_ACTION,
  data
})

// 2. 把 TodoList 文件中 componentDidMount() 的 axios.get() 挪到 actionCreators.js 中
// 3. 在没使用 redux-thunk 之前,我们仅可以在 actionCreators.js 中使用对象,现在我们也可以使用函数了。
export const getTodoList = () => {
  // 7. 当我们使用 getTodoList 的时候,我们也能传递 store 的 dispatch,从而在下面代码中使用
  return (dispatch) => {
    axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolist').then( (res) => {
      // 8. 直接使用 actionCreators.js 中的 initListAction方法,并 dispatch 该 action
      const action = initListAction(res.data.todolist);
      dispatch(action);
    })
  }
}

src/TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 4. 在 TodoList.js 中引用 actionCreators.js 中的 getTodoList
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, getTodoList } from './store/actionCreators';
import TodoListUI from './TodoListUI';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />
    );
  }

  componentDidMount() {
    // 5. 在 componentDidMount 中调用 getTodoList。如果我们没使用 redux-thunk,我们只能使用对象,但是现在我们可以使用函数了。
    const action = getTodoList();
    // 6. 当我们 dispatch 了 action 的时候,我们就调用了步骤 1 的 getTodoList(),从而获取了数据
    store.dispatch(action);
  }

  handleInputChange(e) {
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

  handleStoreChange() {
    this.setState(store.getState());
  }

  handleAddItem() {
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    const action = getDeleteTodoItem(index);
    store.dispatch(action);
  }

}

export default TodoList;

Увидев это, мы, возможно, были ошеломлены, поэтому давайте взглянем на идею:

  1. ПучокaxiosВырезать из TodoList.js в actionCreators.js
  2. Поместите TodoList в файлcomponentDidMount()изaxios.get()Перейти к actionCreators.js
  3. не используетсяredux-thunkРаньше мы могли использовать только объекты в actionCreators.js, теперь мы можем использовать и функции.
  4. Ссылка на actionCreators.js в TodoList.jsgetTodoList(), и удалите те, на которые больше нет ссылокinitListAction
  5. существуетcomponentDidMount()вызыватьgetTodoList(). Если мы не используемredux-thunkМы можем использовать только объекты, но теперь мы можем использовать функции.
  6. когда мыdispatchохватыватьaction, мы называем шаг 1getTodoList(), чтобы получить данные
  7. когда мы используемgetTodoList(), мы также можем пройтиstoreизdispatch, который используется в следующем коде
  8. Непосредственно используйте actionCreators.js вinitListActionметод иdispatchДолженaction

Таким образом, мы проходимredux-thunk,БудуaxiosВызовы интерфейса извлекаются в actionCreators.js.

Почему нам нужно пройти так много шагов, чтобы извлечь его из TodoList.js, который мы хорошо использовали изначально?

На самом деле, что нам нужно знать, так это то, что когда страница достаточно сложная, проект достаточно большой, а кода становится все больше и больше, если все вызовы нашего интерфейса находятся в компоненте-контейнере, нам неудобно управлять интерфейс, и, наконец, если нам нужно изменить определенный интерфейс, мы должны медленно найти его на странице.

пройти черезredux-thunkcall, мы извлекаем код интерфейса из компонента-контейнера, чтобы достичь: логика кода интерфейса — это логика кода интерфейса, а логика бизнес-кода — это логика бизнес-кода.

А такжеredux-thunkИзвлечение может облегчить наше автоматизированное тестирование. Мы, конечно, не знаем, как выглядит автоматизированное тестирование, но можем утешить себя тем, что оно всегда имеет смысл.

Резюме: На этом мы закончили справку и использование Redux-Thunk.Друзья могут попробовать больше и лучше познакомиться с Redux-Thunk.

Fifteen Advanced: промежуточное ПО Redux-Saga для управления запросами Ajax

Назад к содержанию

Имея опыт работы с Redux-Thunk, мы также можем понять Redux-Saga.

Сначала мы все еще копируем файл из Redux-Base в каталог Redux-Saga.

Примечание. Нет необходимости копировать папку node_modules.

  • Установите зависимости:npm i
  • Запустите проект:npm run start

Затем мы начинаем ссылаться на Redux-Saga:

test.js

Сведения о коде
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'

import reducer from './reducers'
import mySaga from './sagas'

// create the saga middleware
const sagaMiddleware = createSagaMiddleware()
// mount it on the Store
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)

// then run the saga
sagaMiddleware.run(mySaga)

// render the application

Пуф, видно, что эталонный метод Redux-Saga такой же простой, как и у Redux-Thunk. Однако, пожалуйста, продолжайте учиться в форме, допускающей некоторую сложность.

Затем мы используем файл index.js в каталоге хранилища для ссылки на Redux-Saga:

src/store/index.js

Сведения о коде
// 1. 引入 applyMiddleware 和 compose 进行多个中间件的处理
import { createStore, applyMiddleware, compose } from 'redux';
import reducer from './reducer';
// 2. 引入 redux-saga 的 createSagaMiddleware
import createSagaMiddleware from 'redux-saga';
// 6. 创建并引用 store 下的 sagas.js 文件
import todoSaga from './sagas';

// 3. 调用 createSagaMiddleware 方法
const sagaMiddleware = createSagaMiddleware();

// 4. 定义 composeEnhancers
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

// 5. 调用 composeEnhancers 进行多中间件处理
const enhancer = composeEnhancers(
  applyMiddleware(sagaMiddleware),
);

const store = createStore(
  reducer,
  enhancer
);

// 7. 使用 todoSaga
sagaMiddleware.run(todoSaga);

export default store;

src/store/sagas.js

Сведения о коде
// 8. 使用 generator 函数定义 todoSaga
function* todoSaga() {

}

// 9. 将 generator 函数导出去
export default todoSaga;

Таким образом, мы завершили референс Redux-Saga и примерно сделали следующие шаги:

  1. представлятьapplyMiddlewareа такжеcomposeПроцесс множественное промежуточное ПО
  2. представлятьredux-sagaизcreateSagaMiddleware
  3. передачаcreateSagaMiddlewareметод
  4. определениеcomposeEnhancers
  5. передачаcomposeEnhancersРазработка нескольких промежуточных программ
  6. Создание и ссылкаstoreфайла sagas.js вtodoSaga
  7. пройти черезsagaMiddlewareиспользоватьtodoSaga
  8. использоватьgeneratorФайл определения функции sagas.js
  9. Будуgeneratorэкспорт функции

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

Ниже мыcomponentDidMount()в методеaxios.get()Эти асинхронные интерфейсы для извлеченияsrc/store/sagas.jsобрабатывается в:

  1. src/TodoList.js
Сведения о коде
import React, { Component } from 'react';
import './index.css';
import 'antd/dist/antd.css';
import store from './store';
// 1. 删除 initListAction 以及下面的 axios,并引入 actionCreators.js 中的 getInitList
import { getChangeInputValue, getAddTodoItem, getDeleteTodoItem, getInitList } from './store/actionCreators';
import TodoListUI from './TodoListUI';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleAddItem = this.handleAddItem.bind(this);
    this.handleInputKeyUp = this.handleInputKeyUp.bind(this);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);

    this.handleStoreChange = this.handleStoreChange.bind(this);
    store.subscribe(this.handleStoreChange);
  }

  render() {
    return (
      <TodoListUI
        inputValue={this.state.inputValue}
        todoList={this.state.todoList}
        handleInputChange={this.handleInputChange}
        handleInputKeyUp={this.handleInputKeyUp}
        handleAddItem={this.handleAddItem}
        handleDeleteItem={this.handleDeleteItem}
      />
    );
  }

  componentDidMount() {
    // 5. 调用 getInitList,并使用 dispatch 将 action 派发出去。这时候不仅 reducer.js 可以接收到这个 action,我们的 sagas.js 也可以接收到这个 action。
    const action = getInitList();
    store.dispatch(action);
  }

  handleInputChange(e) {
    const action = getChangeInputValue(e.target.value);
    store.dispatch(action);
  }

  handleStoreChange() {
    this.setState(store.getState());
  }

  handleAddItem() {
    const action = getAddTodoItem();
    store.dispatch(action);
  }

  handleInputKeyUp(e) {
    if(e.keyCode === 13) {
      this.handleAddItem();
    }
  }

  handleDeleteItem(index) {
    const action = getDeleteTodoItem(index);
    store.dispatch(action);
  }

}

export default TodoList;
  1. src/store/actionCreators.js
Сведения о коде
// 2. 导入 actionTypes.js 中的 GET_INIT_LIST
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM, INIT_LIST_ACTION, GET_INIT_LIST } from './actionTypes';

export const getChangeInputValue = (value) => ({
  type: CHANGE_INPUT_VALUE,
  value
})

export const getAddTodoItem = () => ({
  type: ADD_TODO_ITEM
})

export const getDeleteTodoItem = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})

export const initListAction = (data) => ({
  type: INIT_LIST_ACTION,
  data
})

// 3. 使用 GET_INIT_LIST
export const getInitList = () => ({
  type: GET_INIT_LIST
});
  1. src/store/actionTypes.js
Сведения о коде
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM =  'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
export const INIT_LIST_ACTION = 'init_list_action';
// 4. 定义 GET_INIT_LIST 并导出给 actionTypes.js 使用
export const GET_INIT_LIST = 'get_init_list';
  1. src/store/sagas.js
Сведения о коде
// 6. 引用 redux-saga/effets 中的 takeEvery
// 13. 由于我们在 sagas.js 中没有引用到 store,所以不能使用 store.dispatch(),但是 redux-saga 给我们提供了 put 方法来代替 store.dispatch() 方法
import { takeEvery, put } from 'redux-saga/effects';
// 7. 引入 GET_INIT_LIST 类型
import { GET_INIT_LIST } from './actionTypes';
// 11. 将 TodoList.js 的 axios 引入迁移到 sagas.js 中
import axios from 'axios';
// 12. 引入 actionCreator.js 中的 initListAction
import { initListAction } from './actionCreators'

// 8. 使用 generator 函数
function* todoSaga() {
  // 9. 这行代码表示,只要我们接收到 GET_INIT_LIST 的类型,我们就执行 getInitList 方法
  yield takeEvery(GET_INIT_LIST, getInitList);
}

// 10. 定义 getInitList 方法
function* getInitList() {
  try {
    // 14. 在 sagas.js 中处理异步函数
    const res = yield axios.get('https://www.easy-mock.com/mock/5ca803587e5a246db3d100cb/todolis');
    const action = initListAction(res.data.todolist);
    // 15. 等 action 处理完之后,在执行 put 方法
    yield put(action);
  } catch (error) {
    console.log("接口请求失败,请检查 todolist 接口。");
  }
  
}

export default todoSaga;

Таким образом мы извлекли функцию асинхронного запроса вызывающего интерфейса в файл sagas.js, в ходе чего сделали:

  1. TodoList.js — удалитьinitListActionи следующееaxiosи добавьте getInitList в actionCreators.js.
  2. actionCreators.js — импортировать actionTypes.jsGET_INIT_LIST
  3. actionTypes.js — использоватьGET_INIT_LIST
  4. actionTypes.js — определениеGET_INIT_LISTи экспортируйте в actionTypes.js для использования
  5. TodoList.js — вызовgetInitListи использоватьdispatchБудуactionотправлено. В настоящее время не только reducer.js может получить этоaction, наш sagas.js также может получить этоaction.
  6. Цитироватьredux-saga/effetsсерединаtakeEvery
  7. представлятьGET_INIT_LISTТипы
  8. использоватьgeneratorфункция
  9. пройти черезtakeEvery, указывая на то, что как только мы получимGET_INIT_LISTтип, мы выполняемgetInitListметод
  10. определениеgetInitListметод
  11. Поместите TodoList.jsaxiosПредставить миграцию на sagas.js
  12. Импортировано в actionCreator.jsinitListAction
  13. Так как мы не ссылались на sagas.jsstore, поэтому нельзя использоватьstore.dispatch(),ноredux-sagaпредоставил намputметод заменыstore.dispatch()метод, поэтому мы цитируемputметод.
  14. Обработка асинхронных функций в sagas.js
  15. ЖдатьactionПосле обработки выполнитьputметод:yield put(action)

Таким образом, мы успешно извлекли интерфейс асинхронного запроса из TodoList в sagas.js, чтобы обеспечить единообразное управление интерфейсом.

существуетsrc/store/sagas.js, мы тоже проходимtry...catch...метод для обработки интерфейса.Если интерфейс не существует или запрос ненормальный, мы будем знать, что интерфейс имеет ошибку.

Резюме: На этом мы закончили справку и использование Redux-Saga. Друзья могут попробовать больше и лучше познакомиться с Redux-Saga.

Шестнадцать продвинутых: React-Redux

Назад к содержанию

В предыдущих главах мы использовали React, также использовали Redux и коснулись промежуточного ПО Redux: Redux-Thunk и Redux-Saga.

Итак, в этой главе объясняется React-Redux.

  • Что такое React-Redux.

Это сторонний модуль, который упрощает нам использование Redux в React.

Здесь, поскольку каталог React-Base отделен от React и Redux, мы копируем копию базового кода каталога Simplify в каталог React-Redux и модифицируем TodoList, чтобы начать наше путешествие React-Redux.

Метод преобразования Simplify в TodoList может относиться кГлава 3 Проект инициализации,Глава 4 Использование Ant Designтак же какГлава 5 Использование Redux.

подjsliangОпубликуйте свой собственный код после инициализации:

  1. src/index.js
Сведения о коде
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';

ReactDOM.render(<TodoList />, document.getElementById('root'));
  1. src/TodoList.js
Сведения о коде
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import store from './store';

class TodoList extends Component {

  constructor(props) {
    super(props);
    this.state = store.getState();
  }

  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        <div className="todo-action">
          <Input placeholder='todo' className="todo-input" />
          <Button type="primary" className="todo-submit">提交</Button>
        </div>
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.state.list}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }
}

export default TodoList;
  1. src/index.css
Сведения о коде
.todo {
  width: 1000px;
  margin: 20px auto 0;
  padding: 30px;
  border: 1px solid #ccc;
  border-radius: 10px;
}
.todo-title {
  text-align: center;
}
.todo-action .todo-input {
  width: 200px;
}
.todo-action .todo-submit {
  margin-left: 10px;
}
.todo-list {
  margin-top: 30px;
}
  1. src/store/index.js
Сведения о коде
import { createStore } from 'redux';
import reducer from './reducer';

const store = createStore(reducer);

export default store;
  1. src/store/reducer.js
Сведения о коде
const defaultState = {
  inputValue: '',
  list: [
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

export default (state = defaultState, action) => {
  return state;
}

В этот момент страница отображается как последняя страница главы 4:

  • Реагировать Редукс:Адрес GitHub
  • Установитьreact-redux:npm i react-redux -S

Пришло время показать настоящую технологию!

мы вsrc/index.jsцитируется вreact-redux:

src/index.js

Сведения о коде
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
// 1. 引入 react-redux 的 Provider
import { Provider } from 'react-redux';
// 3. 引入 store
import store from './store';

// 2. 使用 Provider 重新定义 App
const App = (
  // 4. Provider 连接了 store,那么 Provider 里面的组件,都可以获取和使用 store 中的内容
  <Provider store={store}>
    <TodoList />
  </Provider>
)

// 5. 直接渲染 App
ReactDOM.render(App, document.getElementById('root'));

Тогда можетsrc/TodoList.jsиспользуется в:

src/TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
// 6. 在 TodoList 中,我们就不需要使用 import store from store 以及定义 constructor 获取 store 了,而是通过 react-redux 的 connect 来获取
import { connect } from 'react-redux';

class TodoList extends Component {
  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        <div className="todo-action">
          {/* 10. 使用 inputValue */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.props.inputValue}
          />
          <Button type="primary" className="todo-submit">提交</Button>
        </div>
        <div className="todo-list">
          {/* 12. 使用 list */}
          <List
            size="large"
            bordered
            dataSource={this.props.list}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }
}

// 8. 定义 mapStateToProps 方法,把 store 里面的数据,映射成组件里面的 props,其中参数 state 就是 store 里面的数据
const mapStateToProps = (state) => {
  return {
    // 9. 定义 inputValue
    inputValue: state.inputValue,
    // 11. 定义 list
    list: state.list
  }
}

// 7. 导出 connect 方法,让 TodoList 和 store 做连接,需要对应两个规则,即:mapStateToProps 和
export default connect(mapStateToProps, null)(TodoList);

Теперь, когда мы обнаружили, что код все еще работает правильно, давайте проанализируем, какие шаги мы сделали:

  1. представлятьreact-reduxизProvider
  2. использоватьProviderпереопределитьApp
  3. представлятьstore
  4. Providerсвязаныstore,ТакProviderКомпоненты внутри можно получить и использоватьstoreсодержание в
  5. прямой рендерингApp
  6. В TodoList.js нам не нужно использоватьimport store from storeи определениеconstructorПолучатьstore, но черезreact-reduxизconnectполучить
  7. экспортconnectметод, пусть TodoList.js иstoreДля подключения нужно соответствовать двум правилам, а именно:mapStateToPropsа также **
  8. определениеmapStateToPropsметод, поставитьstoreДанные в нем сопоставляются с данными в компонентеprops, где параметрstateто естьstoreданные внутри
  9. определениеinputValue
  10. использоватьinputValue
  11. определениеlist
  12. использоватьlist

Итак, мы закончилиstoreпройти черезreact-reduxсуществуетTodoList.jsцитаты в.

Давайте попробуем изменитьstoreЗначение:

src/TodoList.js

Сведения о коде
import React, { Component } from 'react';
import './index.css';
import { Input, Button, List } from 'antd';
import 'antd/dist/antd.css';
import { connect } from 'react-redux';

class TodoList extends Component {
  render() {
    return (
      <div className="todo">
        <div className="todo-title">
          <h1>TodoList</h1>
        </div>
        <div className="todo-action">
          {/* 3. 给 Input 绑定 onChange 事件 handleInputChange,此时我们通过 this.props 来绑定方法 */}
          <Input 
            placeholder='todo' 
            className="todo-input" 
            value={this.props.inputValue}
            onChange={this.props.handleInputChange}
          />
          <Button type="primary" className="todo-submit">提交</Button>
        </div>
        <div className="todo-list">
          <List
            size="large"
            bordered
            dataSource={this.props.list}
            renderItem={(item, index) => (<List.Item>{index + 1} - {item}</List.Item>)}
          />
        </div>
      </div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    inputValue: state.inputValue,
    list: state.list
  }
}

// 2. 定义 mapDispatchToProps 方法,该方法即是 TodoList.js 将 store.dispatch 方法映射到 props 上,所以我们就可以通过 this.props 来定义方法
// 4. 这里我们传递了 dispatch,所以就可以使用 store.dispatch 方法
const mapDispatchToProps = (dispatch) => {
  return {
    // 5. 定义 handleInputChange 方法
    handleInputChange(e) {
      const action = {
        type: 'change_input_value',
        value: e.target.value
      }
      // 6. 将 action 派发到 reducer.js
      dispatch(action);
    }
  }
}

// 1. 使用 mapDispatchToProps 方法
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

изменениеsrc/reducer.js:

src/reducer.js

Сведения о коде
const defaultState = {
  inputValue: '',
  list: [
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第一条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第二条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第三条 TodoList',
    '这是非常非常非常长的让人觉得不可思议的但是它语句通顺的第四条 TodoList',
  ]
}

export default (state = defaultState, action) => {
  // 7. 判断传递过来的 action.type 是哪个,进行深拷贝,获取 action.value 的值,并返回 newState
  if(action.type === 'change_input_value') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }

  return state;
}

На этот раз мы сделали 7 шагов:

  1. Используется в TodoList.jsmapDispatchToPropsметод
  2. определениеmapDispatchToPropsметод, в котором TodoList.js будетstore.dispatchметод сопоставляется сprops, так что мы можем пройтиthis.propsЧтобы определить методы
  3. ДатьInputсвязыватьonChangeмероприятиеhandleInputChangeВ это время мы прошлиthis.propsсвязать метод
  4. существуетmapDispatchToPropsв котором мы прошлиdispatch, так что вы можете использоватьstore.dispatchметод
  5. определениеhandleInputChangeметод
  6. Будуactionотправить в редуктор.js
  7. решение вынесеноaction.typeЧто это, сделать глубокую копию, получитьaction.valueстоимость и возвратnewState

На этом этапе мы кратко рассмотрели использование React-Redux, Ниже наша кнопка «Кнопка» нажимает кнопку «Отправить» и нажимает элемент «Элемент» для TodoList.listМы не будем подробно объяснять функцию удаления товара, заинтересованные партнеры могут реализовать ее самостоятельно и скачать, скачавjsliangкод для справки:

17 Резюме

Назад к содержанию

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

  1. Использование муравьиного дизайна
  2. Введение и использование Redux
  3. Компоненты пользовательского интерфейса, компоненты-контейнеры, компоненты без сохранения состояния, а также извлечение и упаковка кода для некоторых крупных проектов.
  4. Использование Axios в React
  5. Чтобы упростить управление кодом интерфейса Axios, мы используем промежуточное ПО Redux Redux-Thunk и Redux-Thunk.
  6. Использование React-Redux Повторите использование Redux и изучите использование React-Redux.

На данный момент мы успешно завершили эту статью и перешли к следующему этапу обновления React.

Если ваши друзья чувствуютjsliangХорошо написано, не забудьте поставить [Нравится] или поставитьjsliangНажмите [звездочка] в библиотеке документации, ваш [Нравится] или [звездочка] — моя полная мотивация, ха-ха, увидимся в следующей части серии React!


jsliangРекламный толчок:
Может быть, друг хочет узнать об облачных серверах
Или друг хочет купить облачный сервер
Или маленькому партнеру необходимо обновить облачный сервер
Добро пожаловать, чтобы нажатьПродвижение облачного сервераПроверить!

知识共享许可协议
библиотека документации jsliangЗависит отЛян ЦзюньронгиспользоватьCreative Commons Attribution-NonCommercial-ShareAlike 4.0 Международная лицензияЛицензия.
на основеGitHub.com/l ian Jun Ron…Создание работ выше.
Права на использование, отличные от разрешенных в настоящем Лицензионном соглашении, могут быть получены отCreative Commons.org/licenses/не…получено в.