前言
最近看到很多采用 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 仍然是大型项目中最可靠的保障存在。
以上就是useReducer createContext代替Redux原理示例解析的详细内容,更多关于useReducer createContext代替Redux的资料请关注其它相关文章!
查看更多关于useReducer createContext代替Redux原理示例解析的详细内容...