如何通过useReducer和createContext实现类似Redux的原理示例?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1532个文字,预计阅读时间需要7分钟。
目录前言使用react-redux实现使用react hooks模拟redux实现异步action总结前言最近看到很多使用useReducer+createContext实现一个简易的redux的案例,今天亲自动手试了一下,发现还蛮有意思的。
今天就来分享一下我的实现过程和心得。
使用react-redux实现首先,我们使用react-redux来实现一个简单的计数器应用。
1. 创建一个action类型javascriptconst ACTION_TYPE={ INCREMENT: 'INCREMENT', DECREMENT: 'DECREMENT'};
2. 创建一个reducerjavascriptconst reducer=(state=0, action)=> { switch (action.type) { case ACTION_TYPE.INCREMENT: return state + 1; case ACTION_TYPE.DECREMENT: return state - 1; default: return state; }};
3. 创建一个storejavascriptimport { createStore } from 'redux';const store=createStore(reducer);
4. 在组件中使用javascriptimport React from 'react';import { connect } from 'react-redux';
const Counter=({ count, increment, decrement })=> (
Count: {count}
);const mapStateToProps=state=> ({ count: state});
const mapDispatchToProps=dispatch=> ({ increment: ()=> dispatch({ type: ACTION_TYPE.INCREMENT }), decrement: ()=> dispatch({ type: ACTION_TYPE.DECREMENT })});
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
使用react hooks模拟redux实现接下来,我们使用react hooks来模拟实现一个redux。
1. 创建一个action类型javascriptconst ACTION_TYPE={ INCREMENT: 'INCREMENT', DECREMENT: 'DECREMENT'};
2. 创建一个reducerjavascriptconst reducer=(state=0, action)=> { switch (action.type) { case ACTION_TYPE.INCREMENT: return state + 1; case ACTION_TYPE.DECREMENT: return state - 1; default: return state; }};
3. 创建一个storejavascriptimport { useState, useCallback } from 'react';
const createStore=(reducer)=> { const [state, setState]=useState(reducer(undefined, {})); const dispatch=useCallback((action)=> { setState(prevState=> reducer(prevState, action)); }, [reducer]);
return { state, dispatch };};
const store=createStore(reducer);
4. 在组件中使用javascriptimport React, { useEffect } from 'react';import { useReducer } from 'react';
const Counter=()=> { const [count, dispatch]=useReducer(reducer, 0);
useEffect(()=> { const unsubscribe=store.subscribe(()=> { console.log('store changed', store.getState()); });
return ()=> { unsubscribe(); }; }, []);
return (
Count: {count}
);};异步action在实际应用中,我们经常会遇到异步操作,比如从服务器获取数据。下面我们来实现一个异步action。
1. 创建一个异步actionjavascriptconst fetchData=()=> { return dispatch=> { setTimeout(()=> { dispatch({ type: ACTION_TYPE.FETCH_DATA, payload: 'data from server' }); }, 1000); };};
2. 在组件中使用javascriptimport React, { useEffect } from 'react';import { useReducer } from 'react';
const Counter=()=> { const [count, dispatch]=useReducer(reducer, 0);
useEffect(()=> { dispatch(fetchData()); }, []);
return (
Count: {count}
);};总结通过以上实现,我们可以看到使用react hooks模拟redux的实现方式非常简单,而且可以很好地适应异步操作。在实际项目中,我们可以根据需求选择合适的实现方式。
目录
- 前言
- 采用react-redux实现
- 采用react hooks模拟redux实现
- 异步action
- 总结
前言
最近看到很多采用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的资料请关注自由互联其它相关文章!
本文共计1532个文字,预计阅读时间需要7分钟。
目录前言使用react-redux实现使用react hooks模拟redux实现异步action总结前言最近看到很多使用useReducer+createContext实现一个简易的redux的案例,今天亲自动手试了一下,发现还蛮有意思的。
今天就来分享一下我的实现过程和心得。
使用react-redux实现首先,我们使用react-redux来实现一个简单的计数器应用。
1. 创建一个action类型javascriptconst ACTION_TYPE={ INCREMENT: 'INCREMENT', DECREMENT: 'DECREMENT'};
2. 创建一个reducerjavascriptconst reducer=(state=0, action)=> { switch (action.type) { case ACTION_TYPE.INCREMENT: return state + 1; case ACTION_TYPE.DECREMENT: return state - 1; default: return state; }};
3. 创建一个storejavascriptimport { createStore } from 'redux';const store=createStore(reducer);
4. 在组件中使用javascriptimport React from 'react';import { connect } from 'react-redux';
const Counter=({ count, increment, decrement })=> (
Count: {count}
);const mapStateToProps=state=> ({ count: state});
const mapDispatchToProps=dispatch=> ({ increment: ()=> dispatch({ type: ACTION_TYPE.INCREMENT }), decrement: ()=> dispatch({ type: ACTION_TYPE.DECREMENT })});
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
使用react hooks模拟redux实现接下来,我们使用react hooks来模拟实现一个redux。
1. 创建一个action类型javascriptconst ACTION_TYPE={ INCREMENT: 'INCREMENT', DECREMENT: 'DECREMENT'};
2. 创建一个reducerjavascriptconst reducer=(state=0, action)=> { switch (action.type) { case ACTION_TYPE.INCREMENT: return state + 1; case ACTION_TYPE.DECREMENT: return state - 1; default: return state; }};
3. 创建一个storejavascriptimport { useState, useCallback } from 'react';
const createStore=(reducer)=> { const [state, setState]=useState(reducer(undefined, {})); const dispatch=useCallback((action)=> { setState(prevState=> reducer(prevState, action)); }, [reducer]);
return { state, dispatch };};
const store=createStore(reducer);
4. 在组件中使用javascriptimport React, { useEffect } from 'react';import { useReducer } from 'react';
const Counter=()=> { const [count, dispatch]=useReducer(reducer, 0);
useEffect(()=> { const unsubscribe=store.subscribe(()=> { console.log('store changed', store.getState()); });
return ()=> { unsubscribe(); }; }, []);
return (
Count: {count}
);};异步action在实际应用中,我们经常会遇到异步操作,比如从服务器获取数据。下面我们来实现一个异步action。
1. 创建一个异步actionjavascriptconst fetchData=()=> { return dispatch=> { setTimeout(()=> { dispatch({ type: ACTION_TYPE.FETCH_DATA, payload: 'data from server' }); }, 1000); };};
2. 在组件中使用javascriptimport React, { useEffect } from 'react';import { useReducer } from 'react';
const Counter=()=> { const [count, dispatch]=useReducer(reducer, 0);
useEffect(()=> { dispatch(fetchData()); }, []);
return (
Count: {count}
);};总结通过以上实现,我们可以看到使用react hooks模拟redux的实现方式非常简单,而且可以很好地适应异步操作。在实际项目中,我们可以根据需求选择合适的实现方式。
目录
- 前言
- 采用react-redux实现
- 采用react hooks模拟redux实现
- 异步action
- 总结
前言
最近看到很多采用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的资料请关注自由互联其它相关文章!

