学习阮一峰的 Redux 入门教程(一)笔记

一、使用场景

如果你不知道是否需要 Redux,那就是不需要它

只有遇到 React 实在解决不了的问题,你才需要 Redux

  • 不适用的场景
    • 用户的使用方式非常简单
    • 用户之间没有协作
    • 不需要与服务器大量交互,也没有使用 WebSocket
    • 视图层(View)只从单一来源获取数据
  • Redux 适用场景:多交互、多数据源
    • 用户的使用方式复杂
    • 不同身份的用户有不同的使用方式(比如普通用户和管理员)
    • 多个用户之间可以协作
    • 与服务器大量交互,或者使用了 WebSocket
    • View 要从多个来源获取数据
    • 从组织角度看,适用的场景
      • 某个组件的状态,需要共享
      • 某个状态需要在任何地方都可以拿到
      • 一个组件需要改变全局状态
      • 一个组件需要改变另一个组件的状态

二、设计思想

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

三、基本概念和 API

1、Store


Store: 保存数据的地方,整个应用只能有一个 Store。
Redux 提供 createStore 函数来生成 Store

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

// createStore 函数接受另一个函数作为参数,返回新生成的 Store 对象

2、State


Store 对象包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合叫做 State

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

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

const state = store.getState()

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

3、Action


State 的变化,会导致 View 的变化。但是用户接触不到 State,可以接触到 View,所以 State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了

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

const action = {
    type: 'ADD_TODO',
    payload: 'learn redux'
}

4、Action Creator


定义一个函数来生成 Action,这个函数就叫 Action Creator

const ADD_TODO = '添加 TODO'

function addTodo(text) {
    return {
        type: ADD_TODO,
        text
    }
}
const action = addTodo('learn redux')

5、 store.dispatch()

store.dispatch() 是 View 发出 Action 的唯一方法
store.dispatch 接受一个 Action 对象作为参数,将它发送出去

6、 Reducer


Store 收到 Action 以后,必须给出一个新的 State, 这样 View 才会发生变化,这种 State 的计算过程就叫做 Reducer

Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State

const reducer = function (state, action ) {
    return newState
}

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

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

7、纯函数


Reducer 函数最重要的特征: Reducer 是一个纯函数

  • 纯函数的约束
    • 不得改写参数
    • 不能调用系统 I/O 的 API
    • 不能调用 Date.now() 或者 Math.random() 等不纯的方法,因为每次会得到不一样的结果
// State 是一个对象
function reducer(state, action) {
    return Object.assign({}, state, { thingToChange })
    // 或者
    return { ...state, ...newState }
}

// State 是一个数组
function reducer(state, action) {
    return [...state, newItem]
}

8、store.subscribe()


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

import { createStore } from 'redux'
const store = createStore(reducer)
store.subscribe(listener)

把 View 的更新函数(组件的render、setState)放入 listener ,就会实现 View 的自动渲染

store.subscribe 方法返回一个函数,调用这个函数就可以解除监听

let unsubscribe = store.subscribe(() => {
    console.log(store.getState())
})

四、Store 的实现

  • Store 提供的方法
    • store.getState()
    • store.dispatch()
    • store.subscribe()

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

let store = createStore(todoApp, window.STATE_FROM_SERVER)

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

createStore 的简单实现

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 }
}

五、Reducer 的拆分


Redux 提供了一个方法:combineReducers, 用于 Reducer 的拆分

import { combineReducers } from 'redux'
const chatReducer = combineReducer({
    a: chatLog,
    b: stateMessage,
    c: userName
})
export default todoApp

combineReducer 的简单实现

const combineReducers = reducers => {
    return (state = {}, action) => {
        return Object.keys(reducers).reducer(
            (nextState, key) => {
                nextState[key] = reducers[key](state[key], action)
                return nextState
            }
        )
    }
}

你可以把所有子 Reducer 放在一个文件里,然后统一引入

import { combineReducers } from 'redux'
import * as reducers from './reducers'

const reducer = combineReducers(reducers)

六、工作流程

  • 首先,用户发出Action
store.dispatch(action)
  • 然后,Store 自动调用 Reducer, 并传入两个参数:当前 State 和收到的 Action。Reducer 会返回新的 State
let nextState = todoApp(previousState, action)

State 一旦有变化,Store 就会调用监听函数

// 设置监听对象
store.subscribe(listener)

listener 可以通过 store.getState() 得到当前状态。如果使用的是 React,这时可以触发重新渲染 View

function listener () {
    let newState = store.getState()
    component.setState(newState)
}

你可能感兴趣的:(学习阮一峰的 Redux 入门教程(一)笔记)