前文
react-redux主要提供的功能是将redux和react的组件关联起来。使用提供的connect方法可以使得任意一个react组件获取到全局的store。 实现方法是将store存放于由provider提供的context上,在调用connect时, 就可将组件的props替换, 让其可以访问到定制化的数据或者方法。
目标
本文尝试使用react-hook来替代react-redux的基本功能。
react-redux的特点:
- 全局维护一个store
- 任何组件都可以获得store,最好props可以定制(mapStateProps)
- 提供action的派发能力(mapStateDispatch)
useReducer
先看一下他的内置能力是什么,官网的案例会可我们一些启示能力(也是useState的替代方案)。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
这么看来实际上hook拥有了可以使用redux的机制,状态的派发改变的action,单向的数据流。但是hook不会状态共享,也就是每次useReducer保持是数据状态都是独立的。比如下面这个例子:
function CountWrapper() {
return (
<section>
<Counter initialCount={1}/>
<Counter initialCount={1}/>
</setion>
)
}
两个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];
}
StorePriovider
useReducer实际上不能替代react-redux的全局共享机制,按照react-redux的实现方式来看,是因为提供了一个Provider,使用context的方式来做的,这里使用useContext来弥补这个问题
它本身接收的是一个React.createContext的上下文对象,当provider更新时,会传入store更新时,useContext就可以返回最新的值
import {createContext, useContext} from 'react';
const context = createContext(null);
export const StoreProvider = context.provider;
const store = useContext(context);
useDispatch
到这里我们提供了一个根组件来store, 当store更新时,我们也可以利用useContext也可以拿到最新的值。这个时候暴露出一个hook来返回store上的dispatch即可派发action,来更改state。
export function useDispatch() {
const store = useContext(Context);
return store.dispatch;
}
useStoreState
接下来着眼于组件拿到store上数据的问题,这个其实也很简单,我们都把store拿到了,编写一个自定义hook调用了store.getStore()即可拿到全局的状态。
export function useStoreState(mapState){
const store = useContext(context);
return mapState(store.getStore());
}
这里虽然是把状态拿到了,但忽略了一个非常重要的问题, 当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
}
ok,从这里来看,react-hook的确可以替代react-redux,自己通过简单的封装基本完成了react-redux大部分的功能,但是,换一个场景来说,在大型web服务场景中,react-redux的优化策略和考虑的场景,包括和第三方的框架继承,例如immutable-js, redux-thunk,redux-saga。这样的优化方案,如果你需要自己做集成实现可能成本较高,同时无法考虑全面和对大型web服务的有精准的定位。而且最新的react生态产生的react-server-compoents服务端组件渲染技术,将会减少大量的数据渲染,数据处理的成本,也会在数据维护和性能优化做到一个质的飞跃。但是,中小型的web项目还需要使用react-redux么?实际上不太需要,绝大数据的中小型的web系统,没有太多的复杂交互场景和异步问题,可能只需要借助react-dom和axios就可以完成整个项目的开发了。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!