设计思想

  1. Web应用是一个状态机,视图与状态一一对应
  2. 所有状态都保存在一个对象中

安装

npm install --save redux

附加包

 React 绑定库 & 开发者工具

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

Redux的核心

    store    仓库,保存数据,整个应用只能有一个
    state    状态,state对象包含所有数据
    reducer    修改state的方式,是一个纯函数,接收state与action作为参数,并返回 新的state的函数。对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理 state的一部分。Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
    action     命令,更新state,是一个普通的js对象,用来描述发生了什么,可以清晰地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。如下:
1 { type: 'ADD_TODO', text: 'Go to swimming pool' }
2 { type: 'TOGGLE_TODO', index: 1 }
3 { type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }

 

下面详细阐述一下各个核心

Ⅰ. Store

   生成Store容器:createStore函数接收另外一个函数作为参数,返回一个Store对象。

  import { createStore } from 'redux';
  const store = createStore(fn);

 

Ⅰ. Ⅰ Store 的实现

Store 提供了三个方法。

  • store.getState()

  • store.dispatch()

  • store.subscribe()

  import { createStore } from 'redux';let { subscribe, dispatch, getState } = createStore(reducer);

  createStore方法还可以接受第二个参数,表示 State 的最初状态。这通常是服务器给出的。

  

  let store = createStore(todoApp, window.STATE_FROM_SERVER)

 

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

  下面是createStore方法的一个简单实现,可以了解一下 Store 是怎么生成的。

 

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

 

Ⅱ. State —— 获取:store.getState()

  Store对象包含了所有的数据。而State是Store的数据集合,通过 store.getState() 可以拿到State.

import { createStore } from 'redux';
const store = createStore(fn);// 在生成 Store 的时候,将 Reducer 传入createStore方法

const state = store.getState();

  Redux 规定, 一个 State 对应一个 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什么样,反之亦然。

 1 function visibilityFilter(state = 'SHOW_ALL', action) {
 2   if (action.type === 'SET_VISIBILITY_FILTER') {
 3     return action.filter;
 4   } else {
 5     return state;
 6   }
 7 }
 8 
 9 function todos(state = [], action) {
10   switch (action.type) {
11   case 'ADD_TODO':
12     return state.concat([{ text: action.text, completed: false }]);
13   case 'TOGGLE_TODO':
14     return state.map((todo, index) =>
15       action.index === index ?
16         { text: todo.text, completed: !todo.completed } :
17         todo
18    )
19   default:
20     return state;
21   }
22 }
编写函数以管理state

 

 Ⅲ. Action

   State的变化回导致View的变化,但是用户解除不到View,只能接触到State。所以,State的变化一定是View导致的。

  Action是View发出的通知,表示State将要发生变化了。

   Action 是一个对象。其中的type属性是必须的,表示 Action 的名称。其他属性可以自由设置,社区有一个规范可供参考。

  const action = {
  type: 'ADD_TODO',
  payload: 'Learn Redux'};
  上面代码中,Action 的名称是ADD_TODO,它携带的信息是字符串Learn Redux。
  可以这样理解,Action 描述当前发生的事情。改变 State 的唯一办法,就是使用 Action。它会运送数据到 Store。
 
  • Action Creator

    View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦。可以定义一个函数来生成 Action,这个函数就叫 Action Creator。

    const ADD_TODO = '添加 TODO';
 
    function addTodo(text) {
    return {
    type: ADD_TODO,
    text
    }}
 
    const action = addTodo('Learn Redux');
        以上代码中,addTodo函数就是一个 Action Creator
 
 

Ⅳ. Reducer

  Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。
  Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
 
  const reducer = function (state, action) {
  // ...
  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函数收到名为ADD的 Action 以后,就返回一个新的 State,作为加法的计算结果。其他运算的逻辑(比如减法),也可以根据 Action 的不同来实现。
  实际应用中,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方法的参数。请看下面的例子,一系列 Action 对象按照顺序作为一个数组。
 
const actions = [
{ type: 'ADD', payload: 0 },
{ type: 'ADD', payload: 1 },
{ type: 'ADD', payload: 2 }];

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

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

 

  Ⅳ.Ⅰ  Reducer是一个纯函数

  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 };}
// State 是一个数组
function reducer(state, action) {
return [...state, newItem];}  
  最好把 State 对象设成只读。你没法改变它,要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变的对象。

 

posted on 2021-07-20 14:56  慕华丶  阅读(131)  评论(0)    收藏  举报