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下都有那些核心方法
- createStore, 创建状态 它返回三个函数
subscribe监听状态改变
,getState获取状态数据
,dispatch触发action
- combineReducers, 合并多个reducer
类似于Vuex中 module
- bindActionCreators 将action进行合并调用
- applyMiddleware 中间件 在Vuex中可以使用
Vue.mixin 对Vuex进行拓展
- createStore, 创建状态 它返回三个函数
实现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; // 储存reducerreturn {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 createStoreconst 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 createStoreconst 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文件
// statelet user = {name:'user',age:18}// reducerlet 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;}}// actionconst changenames = (e)=>({type:'C_NAME',preload:e})const stores = createStore(reducers,obj)stores.changenames = changenames;export default stores;// 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;// 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 Appimport 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 Appimport 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 = falselet 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 combineReducersconst 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 combineReducersconst 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 storeslet state = { user1:{ //... }, user2:{ //... } } let user1 = (data ,action)=>{ // .... } let user2 = (data ,action)=>{ // .... } const reducers = combineReducers({ user1,user2 }) const stores = createStore(reducersa,state ); export default storeslet 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;}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; }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.jsconst changenames = (e)=>({type:'C_NAME',preload:e})const bindAction = bindActionCreators({names:(e)=>changenames(e),},stores.dispatch);// App.jsx 使用bindAction.names('users')// 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')// 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,}}}// 管道函数 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, } } }// 管道函数 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的案例改造一下
// 引入thunkimport thunk from "redux-thunk";const stores = createStore(reducersa,state,applyMiddleware(thunk));// 引入thunk import thunk from "redux-thunk"; const stores = createStore(reducersa,state,applyMiddleware(thunk));// 引入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;import { createContext } from "react"; const _context = createContext({}); export default _context;import { createContext } from "react"; const _context = createContext({}); export default _context;
- 创建react-redux/connect.js文件,内容如下
import React, {useContext, useState, useEffect } from 'react';// 核心用的就是 createContextimport 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;}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; }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";// statelet state = {user:{name:'user',age:18}}// reducerlet 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;}}// actionconst changenames = (e)=>({type:'C_NAME',preload:e})// reducersconst reducersa = combineReducers({user})const stores = createStore(reducersa,state,applyMiddleware(thunk));// 合并actionconst 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;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;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 Appimport 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 Appimport 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()*10props.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);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);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);


最后
以上就是关于本片文章全部内容了, 如果文章中存在什么问题并且有什么意见,还请大佬指出,非常感谢!!!
感谢您的来访,获取更多精彩文章请收藏本站。

© 版权声明
THE END
暂无评论内容