useReducer createContext代替Redux原理示例解析
冯心心爱吃肉 人气:0前言
最近看到很多采用useReducer + createContext
实现一个简易的redux
的方案,今天亲自试了一下,发现还是会有一些区别的。
采用react-redux实现
这里使用react-redux
实现一个简单的状态管理例子。
App.jsx根组件
import React from 'react'; import { Button } from './Button'; import { createStore } from 'redux'; import { Provider } from 'react-redux'; import A from './a'; export default function ButtonDemo1() { const reducer = (state, action) => { const { name, theme } = state; switch (action.type) { case 'UPDATENAME': return { ...state, name: `${name} + 1`, }; case 'UPDATETHEME': return { ...state, theme: theme === 'dark' ? 'light' : 'dark', }; default: return state; } }; const store = createStore(reducer, { name: 'fx', theme: 'dark', }); return ( <Provider store={store}> <div> <Button /> <A /> </div> </Provider> ); }
A组件用于dispatch
和接收store
。
A.jsx
import React, { useContext } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { reduxContent } from './index1'; export default function A() { const dispatch = useDispatch(); return ( <div onClick={() => dispatch({ type: 'UPDATENAME' })}> {useSelector((state) => state.name)} </div> ); }
效果如图:
可以看到,Button
组件未使用redux store
,因此正常渲染了一次。
采用react hooks模拟redux实现
这里采用useReducer
+ createContext
模拟实现一个redux
:
App.jsx
import React, { useReducer, createContext } from 'react'; import { Button } from 'concis'; import A from './a'; export const reduxContent = createContext({}); export default function ButtonDemo1() { const reducer = (state, action) => { const { name, theme } = state; switch (action.type) { case 'UPDATENAME': return { ...state, name: `${name} + 1`, }; case 'UPDATETHEME': return { ...state, theme: theme === 'dark' ? 'light' : 'dark', }; default: return state; } }; const [redux, dispatch] = useReducer(reducer, { name: 'fx', theme: 'dark', }); return ( <reduxContent.Provider value={{ redux, dispatch }}> <Button /> <A /> </reduxContent.Provider> ); }
A.jsx
import React, { useContext } from 'react'; import { reduxContent } from './index1'; export default function A() { const { redux, dispatch } = useContext(reduxContent); return ( <div onClick={() => dispatch({ type: 'UPDATENAME' })}> {redux.name} </div> ); }
同样,子组件也可以对store
中的状态进行get
和dispatch
,但是会出现这样的问题:
可以看到,Button
组件并没有使用store
中的内容,但是会随着A
组件一起跟着重新渲染,原因其实就是采用这种方式store
是存储在根组件的,根组件状态发生了变化(useReducer
),子组件跟着一起重新渲染了,因此解决这个问题的思路其实和解决常规的子组件没变化一起被更新的思路是一样的。
可以采用 useMemo
限制 + memo
浅比较。
因此只需要在App.jsx
中这样修改:
const renderButton = React.useMemo(() => { return <Button />; }, []); return ( <reduxContent.Provider value={{ redux, dispatch }}> <div style={{ display: 'flex', flexWrap: 'wrap' }}> {renderButton} <A /> </div> </reduxContent.Provider> ); }
Button.jsx
const Button = (props) => { ...... }) export default React.memo(Button);
异步action
同样,如果需要异步dispatch
的话,简单的场景其实单纯使用异步操作就可以完成,但是在复杂的场景下很难对于异步流进行管理和维护,这时就需要借助redux中间件
了,类似redux-thunk
、redux-saga
,而这也是使用hooks
无法实现的,无法处理副作用,拦截action
去更好的reducer
。
总结
当然,并不是说采用react hooks
所实现的状态管理方式没有好处,这样可以更加贴合react
原生,采用react
自身所提供的hook,并且可以减少项目中的redux
各种实例、减少代码体积,对于小型项目或是不需要很多全局状态的项目,这种方式确实是不错的选择。但是redux
仍然是大型项目中最可靠的保障存在。
加载全部内容