React深入 - 手写redux api

简介: 手写实现redux基础api

createStore( )和store相关方法

api回顾:

createStore(reducer, [preloadedState], enhancer)


创建一个 Redux store 来以存放应用中所有的 state
reducer (Function): 接收两个参数,当前的 state 树/要处理的 action,返回新的 state 树
preloadedState: 初始时的 state
enhancer (Function): store creator 的高阶函数,返回一个新的强化过的 store creator

Store 方法


getState() 返回应用当前的 state 树
dispatch(action) 分发 action。这是触发 state 变化的惟一途径
subscribe(listener) 添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化
replaceReducer(nextReducer) 替换 store 当前用来计算 state 的 reducer(高级不常用,不作实现)实现 Redux 热加载机制会用到


源码实现:


./self-redux.js

export function createStore(reducer, enhancer) {
  if(enhancer) {
     return enhancer(createStore)(reducer)
  }
  let currentState = {}
  let currentListeners = []
  function getState() {
    return currentState
  }
  function subscribe(listeners) {
    currentListeners.push(listener)
  }
  function dispatch(action) {
    currentState = reducer(currentState, action)
    currentListeners.forEach(v => v())
    return action
  }
  dispatch({ type: '@rainie/init-store' })
  return {
    getState,
    subscribe,
    dispatch
  }
}

demo:验证正确性


// import { createStore } from 'redux'
// 将redux文件替换成自己实现的redux文件
   import { createStore } from './self-redux.js'

// 这就是reducer处理函数,参数是状态和新的action
function counter(state=0, action) {
  // let state = state||0
  switch (action.type) {
    case '加机关枪':
      return state + 1
    case '减机关枪':
      return state - 1
    default:
      return 10
  }
}
// 新建store
const store = createStore(counter)
const init = store.getState()
console.log(`一开始有机枪${init}把`)
function listener(){
  const current = store.getState()
  console.log(`现在有机枪${current}把`)
}
// 订阅,每次state修改,都会执行listener
store.subscribe(listener)
// 提交状态变更的申请
store.dispatch({ type: '加机关枪' })

combineReducers(reducers)

api简介


把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数
实现 Redux 热加载机制会用到

import { combineReducers } from 'redux'
import todos from './todos'
import counter from './counter'

export default combineReducers({
  todos,
  counter
})

实现:

实质就是返回一个大的function 接受state,action,然后根据key用不同的reducer
注:combinedReducer的key跟state的key一样


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

function combindReducer(reducers) {
    // 第一个只是先过滤一遍 把非function的reducer过滤掉
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  reducerKeys.forEach((key) => {
      if(typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
      }
  })
  const finalReducersKeys = Object.keys(finalReducers)
    // 第二步比较重要 就是将所有reducer合在一起
    // 根据key调用每个reducer,将他们的值合并在一起
    let hasChange = false;
    const nextState = {};
    return function combind(state={}, action) {
        finalReducersKeys.forEach((key) => {
            const previousValue = state[key];
            const nextValue = reducers[key](previousValue, action);
            nextState[key] = nextValue;
            hasChange = hasChange || previousValue !== nextValue
        })
        return hasChange ? nextState : state;
    }
}


applyMiddleware(...middleware)


使用包含自定义功能的 middleware 来扩展 Redux 是
...middleware (arguments): 遵循 Redux middleware API 的函数。
每个 middleware 接受 Store 的 dispatch 和 getState 函数作为命名参数,并返回一个函数。
该函数会被传入 被称为 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。
调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。
所以,middleware 的函数签名是 ({ getState, dispatch }) => next => action

import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import * as reducers from './reducers'

let reducer = combineReducers(reducers)
// applyMiddleware 为 createStore 注入了 middleware:
let store = createStore(reducer, applyMiddleware(thunk))

中间件机制applyMiddleware的实现

中间件机制图

实现步骤
1.扩展createStore,使其接受第二个参数(中间件其实就是对createStore方法的一次扩展)
2.实现applyMiddleware,对store的disptach进行处理
3.实现一个中间件

正常调用


import React from 'react'
import ReactDOM from 'react-dom'
// import { createStore, applyMiddleware} from 'redux'
import { createStore, applyMiddleware} from './self-redux'
// import thunk from 'redux-thunk'
import thunk from './self-redux-thunk'
import { counter } from './index.redux'
import { Provider } from './self-react-redux';
import App from './App'

const store = createStore(counter, applyMiddleware(thunk))
ReactDOM.render(
  (
    <Provider store={store}>
      <App />
    </Provider>
  ),
  document.getElementById('root')) 

// 便于理解:函数柯利化例子
function add(x) {
  return function(y) {
    return x+y
  }
}
add(1)(2) //3

applymiddleware


// ehancer(createStore)(reducer)
// createStore(counter, applyMiddleware(thunk))
// applyMiddleware(thunk)(createStore)(reducer)
// 写法函数柯利化
export function applyMiddleware(middleware) {
  return function (createStore) {
    return function(...args) {
      // ...
    }
  }
}

// 只处理一个 middleware 时
export function applyMiddleware(middleware) {
   return createStore => (...args) => {
     const store = createStore(...args)
     let dispatch = store.dispatch

     const midApi = {
       getState: store.getState,
       dispatch: (...args) => dispatch(...args)
     }
    // 经过中间件处理,返回新的dispatch覆盖旧的
     dispatch = middleware(midApi)(store.dispatch)
    // 正常中间件调用:middleware(midApi)(store.dispatch)(action)

    return {
      ...store,
      dispatch
    }
   }
 }

// 处理多个middleware时

//  多个 compose
 export function applyMiddleware(...middlewares) {
   return createStore => (...args) => {
     const store = createStore(...args)
     let dispatch = store.dispatch

     const midApi = {
       getState: store.getState,
       dispatch: (...args) => dispatch(...args)
     }

    const middlewareChain = middlewares.map(middleware => middleware(midApi))
    dispatch => compose(...middlewareChain(store.dispatch))
    //  dispatch = middleware(midApi)(store.dispatch)
    // middleware(midApi)(store.dispatch)(action)

    return {
      ...store,
      dispatch
    }
   }
 }

手写redux-thunk异步中间件实现


// middleware(midApi)(store.dispatch)(action)
const thunk = ({ dispatch, getState }) => next => action => {
  // next就是store.dispatch函数
  // 如果是函数,执行以下,参数dispatch和getState
  if (typeof action == 'function') {
    return action(dispatch, getState)
  }
  // 默认 什么都不干
  return next(action)
}
export default thunk

处理异步action
export function addGunAsync() {
  // thunk插件作用,这里可以返回函数
  return dispatch => {
    setTimeout(() => {
      // 异步结束后,手动执行dispatch
      dispatch(addGun())
    }, 2000)
  }
}

趁热打铁,再实现一个中间件: dispatch接受一个数组,一次处理多个action


export arrayThunk = ({ dispatch, getState }) => next => action => {
  if(Array.isArray(action)) {
    return action.forEach(v => dispatch(v))
  }

  return next(action)
}

这类action会被处理
export function addTimes() {
  return [{ type: ADD_GUN },{ type: ADD_GUN },{ type: ADD_GUN }]
}

bindActionCreators的实现

在react-redux connect mapDispatchToProps中使用到了该方法,可以去看那篇blog,有详解~

api: bindActionCreators(actionCreators, dispatch)


把 action creators 转成拥有同名 keys 的对象,但使用 dispatch 把每个 action creator 包围起来,这样可以直接调用它们

实现:


 function bindActionCreator(creator, dispatch) {
   return (...args) => dispatch(creator(...args))
 }

 export function bindActionCreators(creators, dispatch) {
   let bound = {}
   Object.keys(creators).forEach( v => {
     let creator = creators[v]
     bound[v] = bindActionCreator(creator, dispatch)
   })
   return bound
 }
//  简写
 export function bindActionCreators(creators, dispatch) {
  return Object.keys(creators).reduce((ret, item) => {
     ret[item] =  bindActionCreator(creators[item], dispatch)
     return ret
   }, {})
 }

compose的实现

api: compose(...functions)


从右到左来组合多个函数。
当需要把多个 store 增强器 依次执行的时候,需要用到它

import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import DevTools from './containers/DevTools'
import reducer from '../reducers'

const store = createStore(
  reducer,
  compose(
    applyMiddleware(thunk),
    DevTools.instrument()
  )
)

实现:
compose(fn1, fn2, fn3)
fn1(fn2(fn3))


 export function compose(...funcs) {
   if(funcs.length == 0) {
     return arg => arg
   }
   if(funcs.length == 1) {
     return funcs[0]
   }
   return funcs.reduce((ret,item) => (...args) => ret(item(...args)))
 }

原文地址:

原文地址:https://www.cnblogs.com/lalalagq/p/9906529.html

时间: 2024-08-08 18:19:50

React深入 - 手写redux api的相关文章

react纯手写全选与取消全选

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

放弃antd table,基于React手写一个虚拟滚动的表格

缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反而有所上升. 客观地说,antd是开源的,UI设计得比较美观(甩出其他组件库一条街),而且是蚂蚁金服的体验技术部(一堆p7,p8,p9,基本都是大牛级的)在持续地开发维护,质量可以信任. 不过,antd虽好,但一些组件在某一些场景下,是很不适用的.例如,以表格形式无限滚动地展示大量数据(1w+)时,

手写数字识别——利用keras高层API快速搭建并优化网络模型

在<手写数字识别——手动搭建全连接层>一文中,我们通过机器学习的基本公式构建出了一个网络模型,其实现过程毫无疑问是过于复杂了——不得不考虑诸如数据类型匹配.梯度计算.准确度的统计等问题,但是这样的实践对机器学习的理解是大有裨益的.在大多数情况下,我们还是希望能多简单就多简单地去搭建网络模型,这同时也算对得起TensorFlow这个强大的工具了.本节,还是以手写数据集MNIST为例,利用TensorFlow2.0的keras高层API重现之前的网络. 一.数据的导入与预处理 关于这个过程,与上节

关于代码手写UI,xib和StoryBoard

代码手写UI 这种方法经常被学院派的极客或者依赖多人合作的大型项目大规模使用.Geek们喜欢用代码构建UI,是因为代码是键盘敲出来的,这样可以做到不开IB,手不离开键盘就完成工作,可以专注于编码环境,看起来很cool很高效,而且不到运行时大家都不知道会是什么样子,也显出了程序员这一职业的高大上及神秘气息(这个真的不是在黑..想想大家一起在设计师背后指点江山的场景吧).大型多人合作项目使用代码构建UI,主要是看中纯代码在版本管理时的优势,检查追踪改动以及进行代码合并相对容易一些. 另外,代码UI可

代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧

代码手写UI,xib和StoryBoard间的博弈,以及Interface Builder的一些小技巧 最近接触了几个刚入门的iOS学习者,他们之中存在一个普遍和困惑和疑问,就是应该如何制作UI界面.iOS应用是非常重视用户体验的,可以说绝大多数的应用成功与否与交互设计以及UI是否漂亮易用有着非常大的关系.而随着iOS开发发展至今,可以说在UI制作上大家逐渐分化为了三种主要流派:使用代码手写UI及布局:使用单个xib文件组织viewController或者view:使用StoryBoard来通过

手写Maven的archetype项目脚手架

一.背景 maven是一个很好的代码构建工具,采用“约定优先于配置”的原则进行项目管理,相信很多的java开发者应该都了解maven并可能在工作当中都是通过maven来管理项目的,在创建的项目的时候,我们往往会使用maven内置的项目骨架也就是archetype来快速生成项目结构.但是在一个团队做开发的过程中,可能仅仅依靠maven预先提供的archetyp可能是不够的,团队之间协作有自己的定义方式,每个人的结构定义风格也不尽相同,在这样的背景下我们有必要去定义一个统一的代码骨架供团队使用,这样

vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件

vue10行代码实现上拉翻页加载更多数据,纯手写js实现下拉刷新上拉翻页不引用任何第三方插件/库 一提到移动端的下拉刷新上拉翻页,你可能就会想到iScroll插件,没错iScroll是一个高性能,资源占用少,无依赖,多平台的javascript滚动插件.iScroll不仅仅是 滚动.它可以处理任何需要与用户进行移动交互的元素.在你的项目中包含仅仅4kb大小的iScroll,你的项目便拥有了滚动,缩放,平移,无限滚动,视差滚动,旋转功能.iScroll的强大毋庸置疑,本人也非常欢迎大家使用iScr

提高Android应用手写流畅度(基础篇)

在使用android类的手写应用时,整体上都有这样一个印象:android的手写不流畅.不自然,和苹果应用比起来相差太远.本文结合作者亲身经历,介绍一下有效提高手写流畅度的几种方法: 1.未做任何处理的手写效果: 这是一个自定义的view,通过在onTouchEvent时间中捕获系统回调的触摸点信息,然后再onDraw方法里面刷新,可以明显地感觉到线条很生硬,并且在手写的过程中跟随感很差,反应迟钝,具体代码如下: package com.mingy.paint.view; import andr

【转】浅谈React、Flux 与 Redux

本文转自<浅谈React.Flux 与 Redux>,转载请注明出处. React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化 利用 props 形成单向的数据流 根据 state 的变化来更新 view 利用虚拟 DOM 来提升渲染性能 前面说到 React 能够根据 state 的变化来更新 view,一般来说引起 state 变化的动作除了来自外部(如服务器),大部分都来自于页面上的用户活动,那页面上的用户活动怎样对 state 产生作用呢?Reac