xiaochengzi6 / Blog

个人博客
GNU Lesser General Public License v2.1
0 stars 0 forks source link

Redux 的学习笔记 #33

Open xiaochengzi6 opened 2 years ago

xiaochengzi6 commented 2 years ago

学习方法:第一种:][https://github.com/react-guide/redux-tutorial-cn,前六章可以跟着这篇教程学习。后面看[阮一峰](https://github.com/xiaochengzi6/Blog/issues/33#)[http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html]的第一章,在去阅读官网的基础教程无痛入门。第二种就去阅读《react小书》跟着里面讲述redux写一遍简易版的redux再去读官方教程也能更快的入门、推荐第二种

redux核心概念

原理: 应用中所有的state(数据)都以一个对象树的形式存储在一个单一的 store 中,唯一改变的 state 的办法就是触发 action,一个描述发生什么的对象,为了描述 action 如何改变 state 树,你需要编写 Reducer

reducers的参数有 action 和 state 两个

function Reducer (state, action){
    ...
}

注意:state 的结构形式取决于自己,唯一需要记住的是 state 变化时就需要返回全新的对象,(因为 Reducer 是一个纯函数,依赖参数但不修改参数)而让 state 改变的办法只有通过 action 才行(store.dispatch(action)

reducer在没有 state 时会返回一个默认的 state 对象(这里的对象是描述 state 的而不是指它的数据类型如果后面没有备注都是这个意思)你要提前给出默认量。在没有指定 action(描述该发生什么的对象)就不会更新 state 参考下面的代码

// 类似于 action = {type: 'COLOR' value: 'red'}
function Reducer (state, action){
    if (!state){
        return { "默认键值" }
    }
    //在这里我喜欢将之称为reducers规则 以下是判断,可以使用switch也可以使用if之类的。
    switch (action.type) {
        case 'TEXT':
            return {"在此处和state进行合并"}
        ...
        default:
        return state;
    }
}

什么是store

答:store 由 createStore() 函数产生的它处理 reducers 中 state 数据和规则并由此产生了一个数据的集合 store 它可以被看作是一个数据容器store还提供了三个方法用来操作数据。

function createStore(reducer){
    ...
    return {subscribe, dispath, getState}
}

let store = createStore(reducers)

createStore(reducer)创建store产生三个实例方法

store.subscribe(()=>{
    //这里可以将事件绑定到视图层,也可以手动的订阅更新
})
store.dispatch({type: 'COLOR'}) // 唯一改变内部state的方法
store.getState() //获取当前数据

Redux三个原则

单一的数据源

State是只读的

使用纯函数来修改state

Action

Action是数据到store的唯一载体。这句话的意思就是action是唯一可以修改store中的数据的。

//发布 action
store.dispatch(action)  //类似于这种形式

action 本质上是 JavaScript 普通对象(后期可能会由函数生成),约定 action 内必须使用一个字符串类型的 type。action它的目的就是提供确认关系的凭证并给出要修改的数据,action.type 就是一个凭证,而如何确认凭证的就在reducer规则中。

{ type: 'ADD_TODO' ,... }   //action大致模样
{ type: 'ADD_TODO', text: 'value todo' }  

action创建函数 [action function] 它目的返回一个action描述对象 这样更容易编写action和测试

function addTodo(text) {
    return { type: 'ADD_TODO', text: text }
}

action 主要是改变数据用的所以它和dispatch()配合起来用更加方便

//一般用法
function addTodo(text) {
    return { type: 'ADD_TODO', text: text }
}
const add = addTodi('Learn Redux');
store.dispatch(add)
//结合后
const addTodo = text =>dispatch( {
    return { type: 'ADD_TODO', text: text }
})     //注意这个dispatch不能直接使用这里是一个虚构的 dispatch指向的是store.dispatch() 但我们不能直接调用dispatch。继续往下阅读

Reducer

Reducer 指定了应用状态的变化如何响应 action 并发送到 store 的,Reducer 描述了应用如何更新 state

Reducer是纯函数为了保持它的纯度不应该:

修改传入的参数

执行有副作用的操作

调用非纯函数

在 Reducer 首次执行时。state 为 undefined,此时应该设置并返回初始 state

const states ={ value: 'value' }
function Reducer(state, action){
    if(!state)return states;
    ...
}

Reducer 拆分

//state
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
const VisibilityFilter = {
    visibilityFilter: 'SHOW_ALL',
    todo: []
}

//action 1
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}
//action 2
{
  type: TOGGLE_TODO,
  index: 5
}
//action 3
{
  type: SET_VISIBILITY_FILTER,
  filter: SHOW_COMPLETED
}

//reducer
const Reducer = (state = VisibilityFilter, action) =>{
  switch (action.type) {
    // action-3
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    // action-1
      case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })
     // action-2
      case TOGGLE_TODO:
          return Object.assign({}, state, {
            todos: state.todos.map((todo, index) => {
            if (index === action.index) {
                return Object.assign({}, todo, {
                    completed: !todo.completed
            })
          }
          return todo
        })
      })
}

拆分后

function todos(state = [], action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case TOGGLE_TODO:
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: !todo.completed
          })
        }
        return todo
      })
    default:
      return state
  }
}

function visibilityFilter(state = SHOW_ALL, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return action.filter
    default:
      return state
  }
}

进过拆分后最终可以整合成一个大Reducer

function todoApp(state = {}, action) {
  return {
    visibilityFilter: visibilityFilter(state.visibilityFilter,action),
    todos: todos(state.todos, action)
  }
}

最终会返回一个新的 state 每一块属性都由不同的函数去生成。一个 Reducer 可以由多个小的 Reducer 组成。

combineReducers

Redux提供了一个combineReducers 方法,用于将 Reducer 拆分后的代码合并成一个大的 Reducer

import { combineReducers } from 'redux'
const Reducer = combineReducers({
    visibilityFilter,
    todos
})

这样写的前提是state属性和子Reducer同名。否则就要采取以下方式

function reducer(state = {}, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  }
}
const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
})

下面是combineReducer的简单实现

/**
 * 主要功能是返回一个 运行 reducer 的函数 其中会返回新的 state
 * @param {*} reducerMap reducer 数组
 * @returns reducer
 */
function combineReducers(reducerMap) {
  const reducerKeys = Object.keys(reducerMap)

  // 返回 reducer 函数
  const reducer = (state = {}, action) => {
    const newState = {}

    // 主要就是运行所有的 reducer
    for (let i = 0; i < reducerKeys.length; i++) {
      const key = reducerKeys[i]
      const currentReducer = reducerMap[key]
      const preState = state[key]

      newState[key] = currentReducer(preState, action)
    }

    // 返回新的 state
    return newState
  }

  return reducer
}

combindeReducers把一个由多个不同reduce函数作为value的object,合并成一个最终的reducer函数。

import {createStore, combineReducers} from 'redux';
var leftReducer = (state = {}, action) => {
    console.log('1')
    return state
}

var rightReducer = (state = {}, action) => {
    console.log('2');
    return state;
};
var Reducer = combineReducers({
    leftReducer,
    rightReducer
})
//这里为什么会打印两次?
//1
//2
//1
//2

他会接收所有的reducer函数并且还会调用。最后还会将其打包成一个大的Reducer

Store

Redux只有唯一的store。

const store = createStore(Reducer) //创建store

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

let store = createStore(todoApp, window.STATE_FROM_SERVER)

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

Store功能

getState() 方法获取 state

dispatch(action) 方法更新 state

subscribe(listener) 注册监听器

通过 subscribe(listener) 返回的函数注销监听器

/**
 * 生成 store 函数
 * @param {*} state 默认初始值
 * @param {*} stateChange 相当于 Reducer
 * @returns
 */
function createStore(state, stateChange) {
  const listeners = []

  const subscribe = (listener) => listeners.push(listener)

  const getState = () => state

  const dispatch = (action) => {
    stateChange(state, action)
    listeners.forEach((listender) => listender())
  }

  return {
    getState,
    dispatch,
    subscribe,
  }
}

额外的 redux 中间件的学习

/**
 * 处理中间件的函数
 * @param  {...any} middlewares 接收多个中间件
 * @returns 返回 {store, dispatch}
 */
function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    var store = createStore(reducer, preloadedState, enhancer)
    var dispatch = store.dispatch
    var chain = []

    // 传递给中间件的参数
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action),
    }

    // 中间件接收 一个 { getState , dispatch } 的对象
    chain = middlewares.map((middleware) => middleware(middlewareAPI))

    // 这里使用 compose 包装 store.dispatch
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch,
    }
  }
}

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  const last = funcs[funcs.length - 1]
  const rest = funcs.slice(0, -1)

  // [a, b, c, d, e] ==> a(b(c(d(e()))))
  // last(...args) 的返回值 composed 会被 f 包裹起来 然后等于 composed 再被下一个元素包裹起来 一直这样直到结束
  return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
}

// 构建一个中间件
const thunk =
  ({ dispatch, getState }) =>
  (next) =>
  (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState)
    }
    return next(action)
  }

// 为什么要接收一个 next ?
// 因为再 applyMiddleware 中 compose(...chain)(store.dispatch) 做了这样的操作 而 dispatch 也可以被看作是 next