当前位置: 移动技术网 > IT编程>开发语言>JavaScript > redux学习总结与应用

redux学习总结与应用

2020年07月28日  | 移动技术网IT编程  | 我要评论

redux重要知识点

首先,我先给出我学习react框架的参考教程,以下内容都是我依据此教程的个人总结。
redux中文教程
redux 是 JavaScript 状态容器,提供可预测化的状态管理。redux与react之间本身并无直接关系,但是它俩可以完美的搭配使用。简单来说,在流行的MVC三层架构理念中,react主要负责View层数据渲染,那么redux就主要负责Model层的数据存储与一些状态管理。
redux安装:

npm install --save redux

很多时候也许还要安装redux相关的库:

npm install --save react-redux
npm install --save-dev redux-devtools

Redux的三大原则

首先介绍redux的三大原则,其每一条原则都对应于下面的每一个核心:

  • 单一数据源: 简单来说就是在整个应用中只允许有一个store
  • State 是只读的: 即state不允许被更改,若想产生新的state也只能通过action触发。
  • 使用纯函数来执行修改 这里的修改是指产生新的state,而纯函数就是指reducer

也许大家还并不清楚store,action以及reducer究竟是什么,没关系,带着三大原则接着往下看。

Store

store就是redux的数据容器,存储着当前项目里的所有数据,所以有且只有一个。在与react框架结合使用时,store通常是与react框架的顶级父组件绑定在一起。
Talk is cheap,show you the code:

// 创建store
let store = createStore(todoApp)

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

还有几个和store相关的api,这里我先不作讲解,后面内容有用到时再进行讲述。

Action

action是把数据从应用(如果是与react框架结合使用,那么这里的应用可以值react的component组件)传到 store 的有效载荷。它是 store 数据的唯一来源,一般来说是通过 store.dispatch() 将 action 传到 store。
action 本质上是 Object,按照约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作。不同的action的type代表不同的状态,所以state的更新只能由action触发。
下面就是四种不同的action实例:

let nextTodoId = 0;
const TODO_ADD = 'TODO_ADD';
export const actionAddTodo = (todo: IToDo) => ({
  type: TODO_ADD,
  id: nextTodoId++,
  todo
});

const TODO_EDIT = 'TODO_EDIT';
export const actionEditTodo = (id: number, todo: IToDo) => ({
  type: TODO_EDIT,
  id,
  todo
});

const TODO_DELETE = 'TODO_DELETE';
export const actionDeleteTodo = (id: number) => ({
  type: TODO_DELETE,
  id
});

const TODO_STATUS = 'TODO_STATUS';
export const actionSetStatusType = (id: number, status: ToDoStatus) => ({
  type: TODO_STATUS,
  id,
  status
});

Reducer

reducer是干啥的,很简单,就是将action所响应的改变发送给store。reducer里具体实现了如何根据action的状态改变来更新state。
Talk is cheap,show you the example:

const todoList = (state: IReduxState['todoList'] = [], action: { type: string; } & any) => {
  switch (action.type) {
    case 'TODO_ADD':
      return [
        ...state,
        {
          id: action.id,
          title: action.todo.title,
          text: action.todo.text,
          createdTime: String(new Date()),
          expiredTime: action.todo.expiredTime,
          emailAddress: action.todo.emailAddress,
          status: ToDoStatus.New,
        }
      ]
    case 'TODO_EDIT':
      return state.map(todo =>
        (todo.id === action.id)
          ? {
            id: todo.id,
            title: action.todo.title,
            text: action.todo.text,
            createdTime: todo.createdTime,
            expiredTime: action.todo.expiredTime,
            emailAddress: action.todo.emailAddress,
            status: action.todo.status, 
          }
          : todo
      )
    case 'TODO_DELETE':
      return state.filter(todo => todo.id != action.id)
    case 'TODO_STATUS':
      return state.map(todo =>
        (todo.id === action.id)
          ? { ...todo, status: action.status }
          : todo
      )
    default:
      return state
  }
}

我这里给出的一个完整的reducer是完全相对应上面的action。
我怕有朋友会疑惑,这里我说明一个东西:
文中所说的state既是reducer的参数,也是store里的部分数据。而我所说的action的状态是指在reducer里根据不同的action.type来执行不同的更新state的逻辑。
这里再补充一个store与reducer的关系:store的数据储层结构与reducer一一对应。
这是我项目里的store:


export type IReduxState = {
  todoList: Array<IToDo>;
  filterStatus: FilterStatus;
  pageStatus: PageStatus;
  id: number;
}

这是我项目里的最外层reducer:

const todoApp = combineReducers({
  todoList,
  filterStatus,
  pageStatus,
  id,
});

每一个store分支都有对应的reducer,所以所有的state的修改都是在reducer内进行。

Redux的三大核心之间的关系

我以addTodo的操作流程为示例来讲述store,action以及reducer三者之间的关系。
首先由外界发出一个dispatch(actionAddTodo(todo))来告知store要更新数据(注意,此action是携带有todo这个数据),然后reducer根据此action的type知道是addTodo所以就执行’TODO_ADD’的逻辑来更改store里的数据。

react与redux配合使用的流程原理

我们知道react的最核心部分就是组件和每个组件里面的参数props。宏观来看,react根据redux数据render出页面,当要更新数据时,react通知redux更新自己的数据,然后因为redux数据的改变,react所render出的页面也会发生变化。
在redux教程中有一个完整的react与redux结合使用的小案例,大家可以参看着学习。
下面我仍以addTodo功能的实现来讲解react与redux的结合使用。
(我给出的案例功能更强大并且使用的是typescipt语言)
addTodo页面:

import * as React from 'react'
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { actionSetPageType, actionAddTodo } from '../actions/actions';
import { PageStatus, ToDoStatus } from '../interfaces/interfaces';

type AddProps = {
  dispatch?: Dispatch;
}

@(connect() as ClassDecorator)
export default class AddTodo extends React.Component<AddProps> {
  state = {
    title: '',
    text: '',
    expiredTime: '',
    emailAddress: ''
  }
  private jumpToAppPage = () => {
    this.props.dispatch(actionSetPageType(PageStatus.App));
  }

  private changeValue = (e: React.ChangeEvent<HTMLInputElement>) => {
    switch (e.target.name) {
      case 'title':
        this.setState({ title: e.target.value });
        break;
      case 'text':
        this.setState({ text: e.target.value });
        break;
      case 'expiredTime':
        this.setState({ expiredTime: e.target.value });
        break;
      case 'emailAddress':
        this.setState({ emailAddress: e.target.value });
        break;
      default:
        break;
    }
  }

  private createTodo = () => {
    const todo = {
      id: 0,
      title: this.state.title,
      text: this.state.text,
      createdTime: '0000',
      expiredTime: this.state.expiredTime,
      emailAddress: this.state.emailAddress,
      status: ToDoStatus.New
    };
    this.props.dispatch(actionAddTodo(todo));
    this.jumpToAppPage();
  }

  render() {
    return (
      <div>
        <h3>Add</h3>
        <form action="javascript:void(0)" onSubmit={this.createTodo}>
          <label htmlFor="">Title</label>
          <input type="text" value={this.state.title} name='title' onChange={this.changeValue} />
          <br />
          <label htmlFor="">Text</label>
          <input type="text" value={this.state.text} name='text' onChange={this.changeValue} />
          <br />
          <label htmlFor="">ExpiredTime</label>
          <input type="text" value={this.state.expiredTime} name='expiredTime' onChange={this.changeValue} />
          <br />
          <label htmlFor="">EmailAddress</label>
          <input type="text" value={this.state.emailAddress} name='emailAddress' onChange={this.changeValue} />
          <br />
          <input type='submit' value='Create' />
        </form>
        <a href="javascript:void(0)" onClick={this.jumpToAppPage}>Back to List</a>
      </div>
    )
  }
}

此页面由react组件所构成,当页面触发form表单的onSubmit事件时,store会发送dispatch一个action来告知要更新数据。
然后紧接着进行上述的三大关系的流程直至更新store数据,最后由于store数据更新,react所render的页面也会变化。
todoList页面代码:

import * as React from 'react'
import { IReduxState, FilterStatus, ToDoStatus, PageStatus } from '../interfaces/interfaces';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { actionSetPageType, actionDeleteTodo, actionJumpPageId } from '../actions/actions';


const mapStateToProps = (state: IReduxState) => {
  return {
    todoList: state.todoList,
    status: state.filterStatus
  }
}

type TodoListProps = {
  todoList?: IReduxState['todoList'];
  status?: FilterStatus;
  dispatch?: Dispatch;
};
@(connect(mapStateToProps) as ClassDecorator)
export default class TodoList extends React.Component<TodoListProps> {
  private getVisibleTodoS = () => {
    switch (this.props.status) {
      case FilterStatus.New:
        return this.props.todoList.filter(todo => todo.status === ToDoStatus.New);
      case FilterStatus.Done:
        return this.props.todoList.filter(todo => todo.status === ToDoStatus.Done);
      case FilterStatus.Expired:
        return this.props.todoList.filter(todo => todo.status === ToDoStatus.Expired);
      case FilterStatus.All:
      default:
        return this.props.todoList;
    }
  }

  private jumpToEditPage = (id: number) => {
    this.props.dispatch(actionJumpPageId(id));
    this.props.dispatch(actionSetPageType(PageStatus.Edit));
  }

  private deleteHandler = (id: number) => {
    const result = window.confirm('Are you sure?');
    if (result) {
      this.props.dispatch(actionDeleteTodo(id));
    }
  }

  render() {
    const todoList = this.getVisibleTodoS();
    return (
      <table>
        <thead>
          <th>Id</th>
          <th>Title</th>
          <th>Text</th>
          <th>CreatedTime</th>
          <th>ExpiredTime</th>
          <th>EmailAddress</th>
          <th>Status</th>
          <th></th>
        </thead>
        <tbody>
          {todoList.map(todo => (
            <tr>{
              Object.values(todo).map(value =>//TODO status
                <td>{value}</td>
              )
            }
              <td>
                <a href="javascript:void(0)" onClick={() => {
                  return this.jumpToEditPage(todo.id);
                }}>Edit</a>
                {" | "}
                <a href="javascript:void(0)" onClick={() => {
                  return this.deleteHandler(todo.id);
                }}>Delete</a>
              </td>
            </tr>
          ))}
        </tbody>
      </table >
    )
  }
}

结语

其实只要我们弄清楚了react以及redux分别的作用领域,那么将它俩结合在一起使用也就不难。
如果有对此知识点不懂的地方欢迎评论区留言,我看到消息后会及时答复。
最后,由于此项目代码比较多,完整的代码我放在了github上,有兴趣的朋友可以自行下载使用。
在bash命令行中输入下列代码即可运行项目:

npm run dev

源码链接:https://github.com/rookieery/TodoListApp

本文地址:https://blog.csdn.net/asd0356/article/details/107596557

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网