React redux 原理及使用详解

目录
  • 概述
  • createStore创建store
  • applyMiddleware 应用中间件
  • combineReducers 合并多个reducer
  • dispatch
  • 中间件
  • 中间件的调用顺序
  • store
  • redux 数据流
  • bindActionCreators
  • compose
  • enhancer
  • 使用 redux 常遇见的问题

概述

  • 一个状态管理工具
  • Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store。
  • State:包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。
  • Action:Action 就是 View 发出的通知,表示 State 应该要发生变化了。
  • Action Creator:View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦,所以我们定义一个函数来生成 Action,这个函数就叫 Action Creator。
  • Reducer:Store 收到 Action 以后,必须给出一个新的 State。这种 State 的计算过程就叫做 Reducer。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。
  • dispatch:是 View 发出 Action 的唯一方法。

整个工作流程:

  • 首先,用户(通过 View)发出 Action,发出方式就用到了 dispatch 方法。
  • 然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action,Reducer 会返回新的 State
  • State 一旦有变化,Store 就会调用监听函数,来更新 View。

三大原则

1.单一数据源(Store) 整个应用的State被存放在一棵Object tree中,并且这个Object tree只存在唯一一个Store中;

2.State是只读的 唯一改变 State 的方法就是触发 Action,Action 是一个用于描述已发生事件的普通对象。 确保了所有的修改都能被集中化处理。

3.通过纯函数Reducer来修改Store, Reducer 只是一些纯函数,它接收先前的 State 和 Action,并返回新的 State。 即reducer(state, action) => new state

createStore创建store

  • createStore 方法接受 3 个参数参数 (reducer, [preloadedState], enhancer);
    返回 store,store 上挂载着 dispatch、getState、subscribe、replaceReducer 等方法
  • 第二个参数是 preloadedState,它是 state 的初始值,实际上他并不仅仅是扮演着一个 initialState 的角色,如果我们第二个参数是函数类型,createStore 会认为传入了一个 enhancer,如果我们传入了一个 enhancer,createStore 会返回 enhancer(createStore)(reducer, preloadedState)的调用结果,这是常见高阶函数的调用方式。
  • enhancer 接受 createStore 作为参数,对 createStore 的能力进行增强,并返回增强后的 createStore。然后再将 reducer 和 preloadedState 作为参数传给增强后的 createStore,最终得到生成的 store。
export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    // 第二个参数是一个函数,没有第三个参数的情况
    enhancer = preloadedState;
    preloadedState = undefined;
  }
  // 如果第三个参数是函数走下面的逻辑,返回一个新的createStore
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      // enhancer 不是函数就报错
      throw new Error('Expected the enhancer to be a function.');
    }
    // enhancer就是高阶函数,强化了本身这个createStore的函数,拿到增强后的createStore函数去处理
    // applyMiddleware这个函数还会涉及到这个
    return enhancer(createStore)(reducer, preloadedState);
  }
  if (typeof reducer !== 'function') {
    // reducer不是函数报错
    throw new Error('Expected the reducer to be a function.');
  }
  // 其他代码省略
  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable,
  };
}

applyMiddleware 应用中间件

  • 返回一个函数 enhancer;
  • 右边中间件的执行时机由左边的中间件决定,这是因为 next 的方法的调用时机由右边控制
  • dispatch 的处理使用了闭包,这样保证在中间件中调用 dispatch 也是用的最终的 dispatch,也是同一个 dispatch;
  • 写中间件的时候如果要调用 dispatch,一定要有跳出条件,防止死循环
export default function applyMiddleware(...middlewares) {
  return (createStore) =>
    (...args) => {
      const store = createStore(...args);
      let dispatch = () => {
        throw new Error(
          'Dispatching while constructing your middleware is not allowed. ' +
            'Other middleware would not be applied to this dispatch.',
        );
      };
      const middlewareAPI = {
        getState: store.getState,
        dispatch: (...args) => dispatch(...args),
      };
      const chain = middlewares.map((middleware) => middleware(middlewareAPI));
      dispatch = compose(...chain)(store.dispatch);
      return {
        ...store,
        dispatch,
      };
    };
}
// 其实就是修改了dispatch
let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);

combineReducers 合并多个reducer

从执行结果看,这时候 state 已经变成了一个以这些 reducer 为 key 的对象;

reducer 也变成了一个合并的 reducer;

遍历执行所有的 reducer 的时候把 action 传进去,返回新的 state;

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers);
  const finalReducers = {};
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i];
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }
  const finalReducerKeys = Object.keys(finalReducers);
  /* 返回一个整合后的reducers */
  return function combination(state = {}, action) {
    let hasChanged = false;
    const nextState = {};
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i];
      const reducer = finalReducers[key];
      const previousStateForKey = state[key];
      const nextStateForKey = reducer(previousStateForKey, action);
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action);
        throw new Error(errorMessage);
      }
      nextState[key] = nextStateForKey;
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
    }
    return hasChanged ? nextState : state;
  };
}

dispatch

  • 默认的 action 只能是普通对象,除非使用了第三方中间件,比如 redux-thunk
function dispatch(action) {
  if (!isPlainObject(action)) {
    throw new Error(
      'Actions must be plain objects. ' +
        'Use custom middleware for async actions.',
    );
  }
  if (typeof action.type === 'undefined') {
    throw new Error(
      'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?',
    );
  }
  if (isDispatching) {
    throw new Error('Reducers may not dispatch actions.');
  }
  try {
    isDispatching = true;
    currentState = currentReducer(currentState, action);
  } finally {
    isDispatching = false;
  }
  var listeners = (currentListeners = nextListeners);
  for (var i = 0; i < listeners.length; i++) {
    var listener = listeners[i];
    listener();
  }
  return action;
}

中间件

  • Redux 中间件在发起一个 action 至 action 到达 reducer 的之间提供了一个第三方的扩展。本质上通过插件的形式,将原本的 action->redux 的流程改变为 action->middleware1->middleware2-> ... ->reducer,通过改变数据流,从而实现例如异步 Action、日志输入的功能。
  • Redux 中间件范式
    • 一个中间件接收 store 作为参数,会返回一个函数
    • 返回的这个函数接收老的 dispatch 函数作为参数(一般用 next 作为形参),会返回一个新的函数
    • 返回的新函数就是新的 dispatch 函数,这个函数里面可以拿到外面两层传进来的 store 和老 dispatch 函数
function logger(store) {
  return function (next) {
    return function (action) { // 新的 dispatch 函数
      console.group(action.type);
      console.info('dispatching', action);
      let result = next(action);
      console.log('next state', store.getState());
      console.groupEnd();
      return result;
    };
  };
}

中间件的调用顺序

  • 首先调用顺序和书写顺序是一致的,但是这里面的洋葱模型包含了两次顺序,从洋葱出来的顺序是和书写顺序相反
import React from 'react';
import { createStore, applyMiddleware } from 'redux';
function createLogger({ getState, dispatch }) {
  return (next) => (action) => {
    const prevState = getState();
    console.log('createLogger1');
    const returnValue = next(action);
    const nextState = getState();
    const actionType = String(action.type);
    const message = `action ${actionType}`;
    console.log(`%c prev state`, `color: #9E9E9E`, prevState);
    console.log(`%c action`, `color: #03A9F4`, action);
    console.log(`%c next state`, `color: #4CAF50`, nextState);
    return returnValue;
  };
}
function createLogger2({ getState }) {
  return (next) => (action) => {
    const console = window.console;
    const prevState = getState();
    console.log('createLogger2');
    const returnValue = next(action);
    const nextState = getState();
    const actionType = String(action.type);
    const message = `action ${actionType}`;
    console.log(`%c prev state2`, `color: #9E9E9E`, prevState);
    console.log(`%c action2`, `color: #03A9F4`, action);
    console.log(`%c next state2`, `color: #4CAF50`, nextState);
    return returnValue;
  };
}
const reducer = function (state = { number: 0 }, action) {
  switch (action.type) {
    case 'add':
      return {
        number: state.number + action.number,
      };
    default:
      return state;
  }
};
const store = createStore(
  reducer,
  applyMiddleware(createLogger, createLogger2),
);
store.subscribe(function () {
  console.log(1111);
});
const { dispatch } = store;
const App = () => {
  const handler = () => {
    dispatch({ type: 'add', number: 10 });
  };
  return (
    <div>
      <button onClick={handler}>触发redux</button>
    </div>
  );
};
export default App;

store

store 的属性如下:

dispatch: ƒ dispatch(action)

getState: ƒ getState()

replaceReducer: ƒ replaceReducer(nextReducer)

subscribe: ƒ subscribe(listener)

redux 数据流

Redux 的数据流是这样的:

界面 => action => reducer => store => react => virtual dom => 界面

bindActionCreators

将action对象转为一个带dispatch的方法

比如connect接收的mapDispatchToProps 是对象,会使用 bindActionCreators 处理; 接收 actionCreator 和 dispatch,返回一个函数;

function bindActionCreator(actionCreator, dispatch) {
  // 返回一个函数
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}
function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}
const mapDispatchToProps = {  // actionCreators 这是个集合,
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
      filter: filter
  };
}
转换为:
const mapDispatchToProps = {  // actionCreators 这是个集合,
  onClick:function(filter) {
    return dispatch({  // dispatch 是闭包中的方法
      type: 'SET_VISIBILITY_FILTER',
      filter: filter
    })
  }
}

compose

函数套函数,compose(...chain)(store.dispatch)结果返回一个加强了的 dispatch;

这点和koa比较相似,这个 dispatch 在执行的时候会调用中间件。

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  // 每一次reduce迭代都会返回一个加强版的dispatch
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args)),
  );
}

加强版 dispatch(一个方法,接收 action 参数),在中间件中用 next 表示,执行 next 之后,会形成一个链条。

enhancer

  • enhancer,翻译过来就是 store 加强器,比如 applymiddleware 的返回值就是一个 enhaner。store 加强器可以重新构建一个更强大的 store,来替换之前的基础版的 store,让你的程序可以增加很多别的功能,比如 appllymiddleware 可以给你的 redux 增加中间件,使之可以拥有异步功能,日志功能等!
// 以createStore为参数
(createStore) =>
  (...args) => {};

使用 redux 常遇见的问题

  • 样板代码过多 增加一个 action 往往需要同时定义相应的 actionType 然后再写相关的 reducer。例如当添加一个异步加载事件时,需要同时定义加载中、加载失败以及加载完成三个 actionType,需要一个相对应的 reducer 通过 switch 分支来处理对应的 actionType,冗余代码过多;
  • 目前已经存在着非常多的解决方案,比如dva redux-tookit等。
  • 更新效率问题:由于使用不可变数据模式,每次更新 state 都需要拷贝一份完整的 state 造成了内存的浪费以及性能的损耗。
  • 其实 redux 以及 react-redux 中内部已做优化,开发的时候使用 shouldComponentUpdate 等优化方法也可以应用,也可以用不可变数据结构如 immutable、Immr 等来解决拷贝开销问题。

以上就是React redux 原理及使用详解的详细内容,更多关于React redux原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • React中Redux核心原理深入分析

    目录 一.Redux是什么 二.Redux的核心思想 三.Redux中间件原理 四.手写一个Redux 总结 一.Redux是什么 众所周知,Redux最早运用于React框架中,是一个全局状态管理器.Redux解决了在开发过程中数据无限层层传递而引发的一系列问题,因此我们有必要来了解一下Redux到底是如何实现的? 二.Redux的核心思想 Redux主要分为几个部分:dispatch.reducer.state. 我们着重看下dispatch,该方法是Redux流程的第一步,在用户界面中通过

  • React状态管理Redux的使用介绍详解

    目录 1. 简介 2. 核心概念 3. redux工作流 4. 模拟redux工作流程 5. 使用redux 6. react-redux 1. 简介 Redux 是 JavaScript 应用的状态容器(对象),提供可预测的状态管理.可以让你开发出行为稳定可预测的应用,运行于不同的环境(客户端.服务器.原生应用),并且易于测试.Redux 除了和 React 一起用外,还支持其它界面库. 解决的问题:多层级组件间通信问题. 2. 核心概念 单一数据源 整个redux中的数据都是集中管理,存储于

  • 一文详解React Redux使用方法

    目录 一.理解JavaScript纯函数 1.1 纯函数的概念 1.2 副作用概念的理解 1.3 纯函数在函数式编程的重要性 二.Redux的核心思想 2.1 为什么需要 Redux 2.2 Redux的核心概念 2.2.1 store 2.2.2 action 2.2.3 reducer 2.3 Redux的三大原则 2.3.1 单一数据源 2.3.2 State是只读的 2.3.3 使用纯函数来执行修改 2.4 Redux 工作流程 三.Redux基本使用 3.1 创建Store的过程 3.

  • 如何在React中直接使用Redux

    React中使用Redux 开始之前需要强调一下,redux和react没有直接的关系,你完全可以在React, Angular, Ember, jQuery, or vanilla JavaScript中使用Redux. 尽管这样说,redux依然是和React库结合的更好,因为他们是通过state函数来描述界面的状态,Redux可以发射状态的更新, 让他们作出相应; 目前redux在react中使用是最多的,所以我们需要将之前编写的redux代码,融入到react当中去. 这里我创建了两个组

  • React使用redux基础操作详解

    目录 一,什么是redux 二,安装redux谷歌调试工具 三,操作store 改变 四,写redux的小技巧 一,什么是redux Redux是一个用来管理管理数据状态和UI状态的JavaScript应用工具.随着JavaScript单页应用(SPA)开发日趋复杂,JavaScript需要管理比任何时候都要多的state(状态),Redux就是降低管理难度的.(Redux支持React,Angular.jQuery甚至纯JavaScript) react-redux工作流程 安装redux n

  • React Redux应用示例详解

    目录 一 React-Redux的应用 1.学习文档 2.Redux的需求 3.什么是Redux 4.什么情况下需要使用redux 二.最新React-Redux 的流程 安装Redux Toolkit 创建一个 React Redux 应用 基础示例 Redux Toolkit 示例 三.使用教程 安装Redux Toolkit和React-Redux​ 创建 Redux Store​ 为React提供Redux Store​ 创建Redux State Slice​ 将Slice Reduc

  • React redux 原理及使用详解

    目录 概述 createStore创建store applyMiddleware 应用中间件 combineReducers 合并多个reducer dispatch 中间件 中间件的调用顺序 store redux 数据流 bindActionCreators compose enhancer 使用 redux 常遇见的问题 概述 一个状态管理工具 Store:保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store. State:包含所有数据,如果想得到某个时点的数据,就要对

  • React immer与Redux Toolkit使用教程详解

    目录 1. immer 1.1 setState结合immer使用 1.2 useState结合immer使用 1.3 immer和redux集合 2. Redux Toolkit 1. immer 概述: 它和immutable相似的,实现了操作对象的数据共享,可以优化性能.它实现的原理使用es6的Proxy完成的.小巧,没有像immutable哪样有新数据类型,而是使用js类型. 安装: yarn add immer@9 1.1 setState结合immer使用 简单使用: import

  • React DnD如何处理拖拽详解

    目录 正文 代码结构 DndProvider DragDropManager useDrag HTML5Backend TouchBackend 总结 正文 React DnD 是一个专注于数据变更的 React 拖拽库,通俗的将,你拖拽改变的不是页面视图,而是数据.React DnD 不提供炫酷的拖动体验,而是通过帮助我们管理拖拽中的数据变化,再由我们根据这些数据进行渲染.我们可能需要写额外的视图层来完成想要的效果,但是这种拖拽管理方式非常的通用,可以在任何场景下使用.初次使用可能感觉并不是那

  • React中react-redux和路由详解

    目录 观察者模式解决组件间通信问题 react-redux connect方法 Provider组件 路由 使用路由 默认路由 路由重定向 获取路由参数 路由导航 观察者模式解决组件间通信问题 使用观察者解决组件间通信,分成两步 在一个组件中,订阅消息 在另一个组件中,发布消息 发布消息之后,订阅的消息回调函数会执行,在函数中,我们修改状态,这样就可以实现组件间通信了.这就是reflux框架的实现. react-redux redux早期被设计成可以在各个框架中使用,因此在不同的框架中使用的时候

  • 无UI 组件Headless框架逻辑原理用法示例详解

    目录 概述 精读 总结 概述 Headless 组件即无 UI 组件,框架仅提供逻辑,UI 交给业务实现.这样带来的好处是业务有极大的 UI 自定义空间,而对框架来说,只考虑逻辑可以让自己更轻松的覆盖更多场景,满足更多开发者不同的诉求. 我们以 headlessui-tabs 为例看看它的用法,并读一读 源码. headless tabs 最简单的用法如下: import { Tab } from "@headlessui/react"; function MyTabs() { ret

  • GraphQL在react中的应用示例详解

    目录 什么是 GraphQL GraphQL出现的意义 传统API存在的主要问题: GraphQL 如何解决问题 GraphQL基本语法 标量类型 对象类型 枚举类型 GraphQL 内置指令 什么是 Apollo apollo-server 处理流程 1.解析阶段 2.校验阶段 3.执行阶段 Schema 给server端带来的便利性 创建client 将client注入到react 数据请求 数据缓存 apollo-client 总结 GraphQL 的优缺点 优点 缺点 什么是 Graph

  • Apache Cordova Android原理应用实例详解

    目录 前言 技术选型 技术原理 1. 如何本地加载url对应的资源 2. webview如何使用js调用app原生api 3. app原生api如何回调webview中的js 4. 多个plugin的情况 关于踩到的坑 1. 打包路径配置问题 2. success不回调问题 前言 从原理到应用复盘一下自己做过的所有项目,希望能让我自己过两年仍然能看懂现在写的代码哈哈.在项目里我只负责了Android的开发包括插件开发和集成,ios没摸到,有机会也得自己玩下,但是这篇文章不会接触. 技术选型 现在

  • react component function组件使用详解

    目录 不可改变性 虚拟dom与真实dom 函数组件 组件复用 纯函数 组件组合--组件树 组件抽离 不可改变性 1.jsx- 2.component(function)-component(class)-components(函数组件组合)-component tree(redux)-app(项目开发) 在react中,创建了js对象(react元素)就是不可更改的(immutable).就像是用相机拍照,相当于在此时间点已经定位了时间节点,只能拍下一张照片. 例如,使用底层react写一个时钟

  • Redis 实现队列原理的实例详解

    Redis 实现队列原理的实例详解 场景说明: ·用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时 ·高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求 ·抢购场景,先入先出的模式 命令: rpush + blpop 或 lpush + brpop rpush : 往列表右侧推入数据 blpop : 客户端阻塞直到队列有值输出 简单队列: simple.php $stmt = $pdo->prepare('select id, c

随机推荐