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)))
}
原文地址:
React深入 - 手写redux api的更多相关文章
- react深入 - 手写实现react-redux api
简介:简单实现react-redux基础api react-redux api回顾 <Provider store>把store放在context里,所有子组件可以直接拿到store数据 ...
- 手写 redux 和 react-redux
1.手写 redux redux.js /** * 手写 redux */ export function createStore(reducer) { // 当前状态 let currentStat ...
- 手写redux方法以及数组reduce方法
reduce能做什么? 1)求和 2)计算价格 3)合并数据 4)redux的compose方法 这篇文章主要内容是什么? 1)介绍reduce的主要作用 2)手写实现reduce方法 0)了解red ...
- react纯手写全选与取消全选
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 手写一个React-Redux,玩转React的Context API
上一篇文章我们手写了一个Redux,但是单纯的Redux只是一个状态机,是没有UI呈现的,所以一般我们使用的时候都会配合一个UI库,比如在React中使用Redux就会用到React-Redux这个库 ...
- 手写一个Redux,深入理解其原理
Redux可是一个大名鼎鼎的库,很多地方都在用,我也用了几年了,今天这篇文章就是自己来实现一个Redux,以便于深入理解他的原理.我们还是老套路,从基本的用法入手,然后自己实现一个Redux来替代源码 ...
- 放弃antd table,基于React手写一个虚拟滚动的表格
缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...
- 手写React的Fiber架构,深入理解其原理
熟悉React的朋友都知道,React支持jsx语法,我们可以直接将HTML代码写到JS中间,然后渲染到页面上,我们写的HTML如果有更新的话,React还有虚拟DOM的对比,只更新变化的部分,而不重 ...
- 手写数字识别——利用keras高层API快速搭建并优化网络模型
在<手写数字识别——手动搭建全连接层>一文中,我们通过机器学习的基本公式构建出了一个网络模型,其实现过程毫无疑问是过于复杂了——不得不考虑诸如数据类型匹配.梯度计算.准确度的统计等问题,但 ...
随机推荐
- Spring 2.5
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository.@Service 和 @Controller.在目前的 Spring ...
- Codeforces Round #410 (Div. 2) C
Description Mike has a sequence A = [a1, a2, ..., an] of length n. He considers the sequence B = [b1 ...
- AO-XXXX
一 AO4419:应用于开关应用或PWM应用的场效应管.
- AJPFX实现兔子问题
有一对小兔子,从第三个月长成开始每个月生一对小兔子,新出生的小兔子从第三个月长成开始每个月也生一对小兔子,假设所有的兔子都不会死,问每个月兔子的总数?(月数可以是6,12).大神看看我笨方法谢的对吗? ...
- 协程和I/O模型
1.协程: 单线程实现并发 在应用程序里控制多个任务的切换+保存状态 优点: 应用程序级别速度要远远高于操作系统的切换 缺点: 多个任务一旦有一个阻塞没有切换,整个线程都阻塞在原地 该线程内的其他的任 ...
- hihocoder1744 hohahola
思路: 二分. 实现: #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = ...
- ubuntu下php安装目录说明
php当前安装目录 /etc/php5/ apache2: 采用APACHE2HANDLER启动 cli: 采用命令启动 fpm php-fpm启动 fpm2 php-fpm多实例 m ...
- JDBC基础-setFetchSize方法
在Statement和ResultSet接口中都有setFetchSize方法 void setFetchSize(int rows) throws SQLException 查看API文档 Stat ...
- COGS 1361. 树
★ 输入文件:treed.in 输出文件:treed.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 在一个凉爽的夏夜,xth和rabbit来到花园里砍树.为啥 ...
- Servlet 3.0 新特性详解 (转载)
原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性概述 Servlet 3.0 作为 Jav ...