1. 首页

使用react-hook 替代 react-redux

前文

react-redux主要提供的功能是将redux和react链接起来。 使用提供的connect方法可以使得任意一个react组件获取到全局的store。 实现方法是将store存放于由provider提供的context上,在调用connect时, 就可将组件的props替换, 让其可以访问到定制化的数据或者方法。

目标

本文将尝试使用最近很火爆的react-hook来替代react-redux的基础功能。

我们先将理想的特征列举出来,完成这些特性才算是替代了react-redux:

  • 全局维护一个store。
  • 任何组件都可以获取到store,最好props可以定制(mapStatetoProps)。
  • 提供可以派发action的能力(mapDispatchtoProps)。

useRudecer

先看一下内置useRudecer的官方实例能给我们带来一些什么启示:


const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'reset': return initialState; case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: // A reducer must always return a valid state. // Alternatively you can throw an error if an invalid action is dispatched. return state; } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, {count: initialCount}); return ( <div> Count: {state.count} <button onClick={() => dispatch({type: 'reset'})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <div/> ); } // 前端进阶资源教程 https://www.javascriptc.com/

乍一看好像react利用hook已经可以使用redux的机制了, 状态由派发的action改变,单向数据流。但是hook不会让状态共享,也就是每次useReducer保持的数据都是独立的。比如下面这个例子:


function CountWrapper() { return ( <section> <Counter initialCount={1}/> <Counter initialCount={1}/> </setion> ) } // 前端进阶资源教程 https://www.javascriptc.com/

两个Count组件内部的数据是独立的,无法互相影响,状态管理也就无从说起。 究其原因,useReducer内部也是用useState实现的


function useReducer(reducer, initialState) { const [state, setState] = useState(initialState); function dispatch(action) { const nextState = reducer(state, action); setState(nextState); } return [state, dispatch]; } // 前端进阶资源教程 https://www.javascriptc.com/

StoreProvider

useReducer看来并不能帮上忙。解决全局状态的问题可以参照react-redux的做法,提供一个Provider,使用context的方式来做。 这里可以使用useContext,这个内置的hook。

Accepts a context object (the value returned from React.createContext) and returns the current context value, as given by the nearest context provider for the given context. When the provider updates, this Hook will trigger a rerender with the latest context value.

它接受一个由React.createContext返回的上下文对象, 当provider更新时,本文中这里理解为传入的store更新时,useContext就可以返回最新的值。那么我们就有了下面的代码


import {createContext, useContext} from 'react'; const context = createContext(null); export const StoreProvider = context.provider; const store = useContext(context); // do something about store. // 前端进阶资源教程 https://www.javascriptc.com/

useDispatch

到这里我们提供了一个根组件来接受store。当store有更新时,我们也可以利用useContext也可以拿到最新的值。 这个时候暴露出一个hook来返回store上的dispatch即可派发action,来更改state


export function useDispatch() { const store = useContext(Context); return store.dispatch; } // 前端进阶资源教程 https://www.javascriptc.com/

useStoreState

接下来着眼于组件拿到store上数据的问题。这个其实也很简单,我们都把store拿到了,编写一个自定义的hook调用store.getStore()即可拿到全局的状态,


export function useStoreState(mapState){ const store = useContext(context); return mapState(store.getStore()); } // 前端进阶资源教程 https://www.javascriptc.com/

这里虽然是把状态拿到了,但忽略了一个非常重要的问题, 当store上的数据变化时,如何通知组件再次获取新的数据。当store变化过后,并没有和视图关联起来。另一个问题是没有关注mapState变化的情况。 针对第一个问题,我们可以利用useEffect这个内置hook,在组件mount时完成在store上的订阅,并在unmont的时候取消订阅。 mapState的变更可以使用useState来监听, 每次有变更时就执行向对应的setter方法。代码如下


export function useStoreState(mapState) { const store = useContext(context); const mapStateFn = () => mapState(store.getState()); const [mappedState, setMappedState] = useState(() => mapStateFn()); // If the store or mapState change, rerun mapState const [prevStore, setPrevStore] = useState(store); const [prevMapState, setPrevMapState] = useState(() => mapState); if (prevStore !== store || prevMapState !== mapState) { setPrevStore(store); setPrevMapState(() => mapState); setMappedState(mapStateFn()); } const lastRenderedMappedState = useRef(); // Set the last mapped state after rendering. useEffect(() => { lastRenderedMappedState.current = mappedState; }); useEffect( () => { // Run the mapState callback and if the result has changed, make the // component re-render with the new state. const checkForUpdates = () => { const newMappedState = mapStateFn(); if (!shallowEqual(newMappedState, lastRenderedMappedState.current)) { setMappedState(newMappedState); } }; // Pull data from the store on first render. checkForUpdates(); // Subscribe to the store to be notified of subsequent changes. const unsubscribe = store.subscribe(checkForUpdates); // The return value of useEffect will be called when unmounting, so // we use it to unsubscribe from the store. return unsubscribe; }, [store, mapState], ); return mappedState } // 前端进阶资源教程 https://www.javascriptc.com/

如上就完成了hook对react-redux的功能重写,从代码量来说是简洁量不少,并且实现方式也更贴合react未来的发展方向。 可见大概率上react-redux会被hook的方式逐渐替代。本文是对redux-react-hook实现的原理讲解,想要在线尝试本文所诉内容点击这个codesandbox

作者:是熊大啊
链接:https://juejin.im/post/5bf991236fb9a049f570ce1c

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:使用react-hook 替代 react-redux

链接:https://www.javascriptc.com/3818.html

« 打造工业级推荐系统(六):构建优质的推荐系统服务
React Hook的实现原理和最佳实践»
Flutter 中文教程资源

相关推荐

QR code