Redux 知识点梳理(上)

1,178 阅读4分钟

Redux 设计思想

  • web 应用是一个状态机,视图与状态一一对应。
  • 所有的状态,保存在一个对象里面。

基本概念

Store

store 就是保存数据的地方。整个应用只有一个 Store。

Redux 提供了 createStore 函数,用来生成 Store。

import { createStore } from "redux";

const store = createStore(fn);

上面代码,createStore函数接受一个函数作为参数,返回新生成的 Store 对象。

State

Store对象包含所有数据State是某个时间点,Store 生成的快照数据集合

当前时刻的 State 可以通过store.getState()拿到。

import { createStore } from "redux";

const store = createStore(fn);

const state = store.getState();

Redux 规定,一个 state 对应一个 view 。只要 state 相同,view 就相同,state 反应 当前 view 的情况。

Action

State 的变化,会导致 view 的变化。但是,用户接触不到 state 的 变化,只能接触到 View 。

所以 view 的变化必然是 State 的变化导致的。

Action 就是 view 发出的通知,表示 State 应该要发生变化了。

Action 是一个对象。 其中的 type 属性是必须的,表示 Action 的名称。

const action = {
  type: "ADD_TODO",
  payload: "Learn Redux"
};

上面的代码中,Action 的名称是 “ADD_TODO”,它携带的信息是字符串"Learn Redux"。

Actions 是改变 State 的唯一方法,它描述了当前发生的事情。

Action creator

import { ADD } from "../constants/counter";

// 定义Action Creator
export const add = () => {
  return {
    type: ADD
  };
};

store.dispatch()

store.dispatch 是 view 发出 Action 的唯一方法。

import { createStore } from "redux";

const store = createStore(fn);
// 调用 Action
store.dispatch({
  type: "ADD_TODO",
  payload: "Learn Redux"
});

上面的代码,store.dispatch() 接受一个 Action 对象作为参数,将它发送出去。

import { createStore } from "redux";

const store = createStore(fn);
// 调用 Action
store.dispatch(add());

Reducer

Store 收到 Action以后,必须给出一个变化的新的 State,这样View才会发生变化。

这种给出变化的新的 State 的计算过程,就叫做Reducer

Reducer 是一个函数,他接受 Action 和当前时间点的对象集合 State作为参数,返回一个 新的 State。

const reducer = function(state, action) {
  // TODO
  return new_state;
};

整个应用的初始状态,可以作为 State 的默认值。下面是一个实际的例子。

const defaultState = 0;
const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case "ADD":
      return state + action.payload;
    default:
      return state;
  }
};

const state = reducer(1, {
  type: "ADD",
  payload: 2
});

上面的代码中,reducer函数收到名为ADDAction以后,就会返回一个新的 State,作为加法的结果。

实际开发中,Reducer 函数不用手动调用, store.dispatch方法 会触发 Reducer 的自动执行。为此,Store 需要知道 Reducer 函数,做法就是在生成 Store 的时候,将 Reducer 传入 createStore 方法。

import { createStore } from "redux";
const store = createStore(reducer);

上面的代码中,createStore接受reducer函数作为参数,生成一个新的 Store。以后每当store.dispatch发送一个新的Action,就会自动调用Reducer,得到新的 state。

为什么交 Reducer 呢?因为他可以作为数组的 reduce 方法的参数。


const actions=[
  {type:"ADD",0},
  {type:"ADD",1},
  {type:"ADD",2}
];

const total=actions.reduce(reducer,0) //3

上面代码中,数组 actions 表示依次有三个 Action,分别是加 0、加 1 和加 2。数组的 reduce 方法接受 Reducer 函数作为参数,就可以直接得到最终的状态 3。

纯函数

Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。

纯函数是函数式编程的概念,必须遵守以下一些约束。

  1. 不得改写参数
  2. 不能调用系统 I/O 的 API
  3. 不能调用 Date.now() 或者 Math.random() 等不纯的方法,因为每次的到的结果不一样。

由于 Reducer 是纯函数,就可以保证同样的 State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象。

// State 是一个对象
function reducer(state, action) {
  return Object.assign({}, state, { thingToChange });
  // 或者
  return { ...state, ...newState };
}

store.subscribe()

Store 允许使用 store.subscribe()方法设置监听函数,一旦 State 发生变化,就会自动调用执行这个函数。

import { createStore } from "redux";

const store = createStore(reducer);

store.subscribe(listener);

显然,只要把 View 的更新函数(对于 React 项目,就是组件的 render 方法或 setState 方法)放入 listen,就会实现 View 的自动渲染。

Store 的实现

综上,可以发现 Store 提供了三个方法。

  • store.getState();
  • store.dispatch();
  • store.subscribe();
import { createStore } from "redux";

const { getState, dispatch, subscribe } = createStore(reducer);

createStore 方法还可以接受第二个参数,表示 State 的最初状态。

let store = createStore(reducer, window.STATE_FROM_SERVER);

上面代码中,window.STATE_FROM_SERVER 就是整个应用的状态初始值。注意,如果提供了这个参数,它会覆盖 Reducer 函数的默认初始值。

const createStore = reducer => {
  let state;
  let listeners = [];
  const getState = () => state;
  const dispatch = action => {
    // 生成新的 state
    state = reducer(state, action);
    // ??
    listeners.forEach(listener => listener);
  };
  const subscribe = listener => {
    // ??
    listeners.push(listener);
    // ???
    return () => {
      listener = listeners.filter(l => l !== listener);
    };
  };
git
  dispatch({});

  return {
    getState,
    dispatch,
    subscribe
  };
};