redux ,react-redux 源码实现


theme: smartblue highlight: an-old-hope

在react中,redux 可以说是一个管理全局状态的状态机,但是状态管理的方式不只有redux,还有mobx,等待其他的状态管理的库。下面我们先了解一下我们为什么需要对项目做状态管理,以及如何实现redux的。
  • redux可以在其他框架中使用吗?
当我们,redux是一套与ui相互独立的库,也就是说他不依赖任何前端框架,在Vue中也可以直接使用,但是Vuex是不可以在其他框架中使用的,因为Vuex是基于Vue开发的。
  • 为什么要redux状态管理?
比如我们有A,B,C三个个容器,这三个容器中的状态都是相同的,其中一个容器做出改变,另外几个容器需要进行同步改变,这里最简单的实现就是在最外层包裹一层容器F,将状态保存在这个容器中,这样做可以解决当前问题,但是后面的问题来了,如果后期我们客户需要新增一个容器E这个容器需要的状态跟A一样并且不能创建在F容器下,其实拿到状态也不是不可以就是这样做我们的代码会越来越复杂,如果使用redux状态管理就很完美的解决上面的问题,首先redux在全局抛出一个state,并且在项目中的任何组件中都可以获取这个状态,通过某个组件做出改变后同步到其他依赖的组件。
  • redux下都有那些核心方法
    1. createStore, 创建状态 它返回三个函数subscribe监听状态改变,getState获取状态数据,dispatch触发action
    2. combineReducers, 合并多个reducer 类似于Vuex中 module
    3. bindActionCreators 将action进行合并调用
    4. applyMiddleware 中间件 在Vuex中可以使用 Vue.mixin 对Vuex进行拓展

实现createStore,下面源码是根据redux官方源码简化实现,所以还是存在差别的

  • 创建文件 redux/createStore.js
  • 官方地址redux

const createStore = (reducer,preloadedState,enhancer)=>{
//  这里先写出来后面做解释  enhancer 处理中间件
if(enhancer){
return enhancer(createStore)(reducer,preloadedState)
}
let currentState = preloadedState // 初始状态
let currentListenter= [];
let currentReducer = reducer;  // 储存reducer
return {
subscribe:(func)=>{
currentListenter.push(func)
},
getState:()=>{
return JSON.parse(JSON.stringify(currentState))
},
dispatch:(action:{type,preload})=>{
currentState = currentReducer(currentState,action);
currentListenter.map(fn=>fn(currentState));
}
}
}
export default createStore
根据上面的源码可以看出createStore核心实现就是一个发布订阅,现在问题来了,代码这么少能用吗?那必须能用啊,下面来个例子
  • 首先创建store/index.js文件
// state
let user = {
name:'user',
age:18
}
// reducer
let  user = (data ,action)=>{
switch(action.type){
case 'C_NAME':
return {...data,name:action.preload};
case 'C_AGE':
return {...data,age:action.preload};
default:
return data;
}
}
// action
const changenames = (e)=>({type:'C_NAME',preload:e})
const stores = createStore(reducers,obj)
stores.changenames = changenames;
export default stores;
  • 在App.jsx中引入
import React, { useState,useEffect } from 'react'
import stores from './store';
function App() {
let [infos,setInfos] = useState();
const changeName = ()=>{
stores.dispatch(stores.changenames('摸鱼的汤姆'))
}
const changeAppname = (e)=>setName(e)
useEffect(()=>stores.subscribe((e)=>changeAppname),[])
return (
<div>
{infos.name}
<button onClick={changeName}>点击</button>
</div>
)
}
export default App
  • 最后视图效果就是name的值改变为了摸鱼的汤姆
刚开始的时候说过redux一些核心方法,但我们只实现了一个,下面就是将我们的redux继续完善一下

实现combineReducers

  • 创建文件 redux/combineReducers.js
const combineReducers = (reducers:any)=>{
if(!isPlanObject(reducers)){
throw console.error('reducers is not a Object!');
}
// 为了第二次调用直接获取
const reducerKeys =   Object.keys(reducers);
return function combination(state={},action){
let hasChanged = false
let nextstate = {};
reducerKeys.forEach(item=>{
const key = reducers[item];
// 获取旧值
const preDataKey =  state[item];
const nextDataKey = key(preDataKey,action);
// 赋值新值
nextstate[item] = nextDataKey;
// 新旧对比判断值是否有变
hasChanged = hasChanged || preDataKey !== nextstate
})
return hasChanged?nextstate:state
}
}
export default combineReducers
  • 使用起来就比较简单了,看下面的例子
let state = {
user1:{
//...
},
user2:{
//...
}
}
let  user1 = (data ,action)=>{
// ....
}
let  user2 = (data ,action)=>{
// ....
}
const reducers =  combineReducers({
user1,user2
})
const stores = createStore(reducersa,state );
export default stores
combineReducers 主要的作用就是将多个reducer进行合并,对状态进行更改

实现bindActionCreators

  • 创建文件 redux/bindActionCreators.js
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments))
}
}
const bindActionCreators = (actionCreators,dispatch)=>{
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {};
for(let key in actionCreators){
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators;
}
  • bindActionCreators使用案例
// store/index.js
const changenames = (e)=>({type:'C_NAME',preload:e})
const bindAction = bindActionCreators({
names:(e)=>changenames(e),
},stores.dispatch);
// App.jsx 使用
bindAction.names('users')

实现applyMiddleware

  • 创建文件 redux/applyMiddleware.js
// 管道函数
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// 中间件就是一个洋葱模型,中间件会根据顺序一层一层的向后调用
const applyMiddleware = (...middlewares)=>{
// 这里的柯里化就是开始的时候enhancer(createStore)(reducer,preloadedState)的enhancer调用
return (createStores)=>(...args)=>{
const store = createStores(...args)
let dispatch = (e)=>{};
const middlewareAPI = {
getState: store.getState,
dispatch: (...arg) => dispatch(...arg),
}
const chain = middlewares.map((middleware) => middleware(middlewareAPI))
// 将所有中间件放入到管道中调用
dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch,
}
}
}
  • 使用redux-thunk中间件
redux-thunk主要用于异步调用action,关于redux-thunk的源码我们就不分析了,感兴趣的可以去看看源码,下面我们就安装一下
  • 执行x-thunk npm install redux-thunk
  • 将上面combineReducers的案例改造一下
// 引入thunk
import thunk from "redux-thunk";
const stores = createStore(reducersa,state,applyMiddleware(thunk));

实现react-redux

  • 上面关于redux的核心方法都已经实现了,下面开始实现一下react-redux
首先我们分析一下我们在使用react-redux都是引入react-redux中的Provider包裹在最外层,然后里面需要使用store的使用connect对子组件进行包裹。其实其中的原理也不是很复杂主要是用到了createContext去实现的,那下面就开始上代码!
  • 创建react-redux/context.js文件,内容如下
import { createContext } from "react";
const _context = createContext({});
export default _context;
  • 创建react-redux/connect.js文件,内容如下
import React, {useContext, useState, useEffect } from 'react';
// 核心用的就是 createContext
import ReduxContext from './context';
let key = false;
export const connect = (mapStateToProps,mapDispatchToProps)=>Component=>{
function Connect(props){
const store = useContext(ReduxContext);
const [count,setCount] = useState(false);
const forceUpdata = ()=>{
setCount(val=>!val);
};
//这里是核心,因为redux更改是单项数据流,不会触发页面更新,所以这里需要监听一下更新count,触发视图更新
useEffect(()=>store.subscribe((e)=>{
forceUpdata()
}),[]);
return (
<ReduxContext.Consumer>
{
store => <>
<Component
{...props}
{...mapStateToProps(store.getState())}
{...mapDispatchToProps(store.dispatch)}
>
</Component>
</>
}
</ReduxContext.Consumer>
)
}
return Connect;
}

redux,react-redux都实现了,下面就来一个完整案例把

  • 完整store案例
import { createStore,combineReducers,bindActionCreators,applyMiddleware } from "./redux/createStore";
import thunk from "redux-thunk";
// state
let state = {
user:{
name:'user',
age:18
}
}
// reducer
let  user = (data ,action)=>{
switch(action.type){
case 'C_NAME':
return {...data,name:action.preload};
case 'C_AGE':
return {...data,age:action.preload};
default:
return data;
}
}
// action
const changenames = (e)=>({type:'C_NAME',preload:e})
// reducers
const reducersa =  combineReducers({
user
})
const stores = createStore(reducersa,state,applyMiddleware(thunk));
// 合并action
const bindAction = bindActionCreators({
names:(e)=>changenames(e),
},stores.dispatch);
// thunk用法 -- thunk 的原理比较简单就不实现了
const logins = (name)=>(dispatch)=>{
setTimeout(()=>{
dispatch(changenames(name))
},1000)
}
stores.dispatch(logins('摸鱼的汤姆'))
export default stores;
  • 更改App.jsx
import React, { useState,useEffect } from 'react'
import stores from './store';
import ReduxContext from './redux/context';
import Child from './Child';
function App() {
return (
<ReduxContext.Provider value={stores}>
<Child/>
</ReduxContext.Provider>
)
}
export default App
  • 创建Child.jsx文件

import React,{useEffect} from 'react';
import { connect } from './redux/connectValue';
const Child = (props)=>{
const changeusername = ()=>{
const nums = Math.random()*10
props.changeName(nums)
console.log(props.user.name)
}
return (
<>
{props.user.name}
<div onClick={()=>changeusername()}>
asdasd
</div>
</>
)
}
const mapStateToProps = (state)=>({...state});
const mapDispatchToProps = (dispatch)=>{
return {
changeName:(e)=>dispatch({type:'C_NAME',preload:e})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Child);
最后效果
image.png
点击改变之后
image.png

最后

以上就是关于本片文章全部内容了, 如果文章中存在什么问题并且有什么意见,还请大佬指出,非常感谢!!!
------本页内容已结束,喜欢请分享------

感谢您的来访,获取更多精彩文章请收藏本站。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容