react hooks 全面转换攻略(三) 全局存储解决方案
针对 react hooks 的新版本解决方案
一.redux维持原方案
若想要无缝使用原来的 redux,和其配套的中间件 promise,thunk,saga 等等的话
可以使用 redux-react-hook
github 链接 redux-react-hook
一个简单的使用例子:
import {useDispatch, useMappedState} from 'redux-react-hook'; export function DeleteButton({index}) { // 类似于以前 react-redux 中的 connect 函数 const mapState = useCallback( state => ({ canDelete: state.todos[index].canDelete, name: state.todos[index].name, }), [index], ); // 获取 redux 的数据 const {canDelete, name} = useMappedState(mapState); // 获取 dispatch const dispatch = useDispatch(); // button click handle const deleteTodo = useCallback( () => dispatch({ type: 'delete todo', index, }), [index], ); return ( <button disabled={!canDelete} onClick={deleteTodo}> Delete {name} </button> ); }
使用方法和以前一致
二.使用 useReducer 与 context
在 index 或 app 中提供全局的 redux 与 dispatch
function isPromise(obj) { return ( !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function" ); } function wrapperDispatch(dispatch) { // 功能和 redux-promise 相同 return function (action) { isPromise(action.payload) ? action.payload.then(v => { dispatch({type: action.type, payload: v}) }).catch((error) => { dispatch(Object.assign({}, action, { payload: error, error: true })); return Promise.reject(error); }) : dispatch(action); }; } function Wrap(props) { // 确保在 dispatch 后不会刷新APP组件 const [state, dispatch] = useReducer(reducers, ReducersValue); console.log('render wrap') return (<MainContext.Provider value={{state: state, dispatch: wrapperDispatch(dispatch)}}>{props.children}</MainContext.Provider>) } function App() { console.log('render App') return <Wrap> <Router> <Switch> <Route path="/login" component={Login} exact/> <Route path="/" component={MainIndex}/> </Switch> </Router> </Wrap> }
具体使用:
function useDispatch() { // 获取 dispatch const store = useContext(MainContext); return store.dispatch; } function useStoreState(mapState) { //存储 state 且判断是否需要 render const {state:store} = useContext(MainContext); const mapStateFn = () => mapState(store); const [mappedState, setMappedState] = useState(() => mapStateFn()); const lastRenderedMappedState = useRef(); // Set the last mapped state after rendering. useEffect(() => { lastRenderedMappedState.current = mappedState; }); useEffect( () => { console.log('useEffect ') const checkForUpdates = () => { const newMappedState = mapStateFn(); if (!_.isEqual(newMappedState, lastRenderedMappedState.current)) { setMappedState(newMappedState); } }; checkForUpdates(); }, [store, mapState], ); return mappedState } // 组件内使用 const ResourceReducer = useStoreState(state => state.ResourceReducer) const dispatch = useDispatch()
他的功能已经足够了,在使用的地方使用函数即可,很方便
但是也有一些不足的地方是在根源上的,即 context,
在同一个页面中 如果有多个使用 context 的地方
那么如果一旦dispatch ,其他的所有地方也会触发render 造成资源的浪费,小项目还好,大项目仍旧不可
取
(除非 react 的 context 函数添加 deps)
三.自定义解决方案
原理就是存储一个全局变量 ,通过 import 引入;
我自己写了一个例子:https://github.com/Grewer/react-hooks-store
想要基础的实现只需要 30+ 行的代码即可
class Modal { private value: any; private prevValue: any; private reducers: (state, action) => {}; private queue: any = []; private dispatch: (action) => void; constructor(reducers) { this.reducers = combineReducers(reducers) // combineReducers 来自于 reudx ,可以引入也可以自己写一个(后续我会写一个库,会包含此函数) this.value = this.reducers({}, {}) this.dispatch = action => { this.prevValue = this.value; this.value = this.reducers(this.value, action) this.onDataChange() } } useModal = (deps?: string[]) => { const [, setState] = useState(this.value); useEffect(() => { const index = this.queue.push({setState, deps}); // 订阅 return () => { // 组件销毁时取消 this.queue.splice(index - 1, 1); }; }, []); return [this.value, this.dispatch] } onDataChange = () => { this.queue.forEach((queue) => { const isRender = queue.deps ? queue.deps.some(dep => this.prevValue[dep] !== this.value[dep]) : true isRender && queue.setState(this.value) }); } }
// 初始化 reducers
const modal = new Modal({ countReducer: function (state = 0, action) { console.log('count Reducer', state, action) switch (action.type) { case "ADD": console.log('trigger') return state + action.payload || 1 default: return state } }, listReducer: function (state = [] as any, action) { console.log('list Reducer', state, action) switch (action.type) { case "ADD_LIST": console.log('trigger') state.push(action.payload) return [...state] default: return state } }, personReducer: function (state = {name: 'lll', age: 18} as any, action) { console.log('person Reducer', state, action) switch (action.type) { case "CHANGE_NAME": return Object.assign({}, state, {name: action.payload}) default: return state } } }) // 导出 useModal export const useModal = modal.useModal
简单的使用:
function Count(props) { const [state, dispatch] = useModal(['countReducer']) // 非 countReducer 的更新 不会触发此函数 render console.warn('render Count', state, dispatch) return <div> <button onClick={() => dispatch({type: "ADD", payload: 2})}>+</button> </div> }
当然你也可以自己写一个,自己想要的方案
总结
hooks 的存储方案基本就这 3 类,可以用现成的,也可以使用自己写的方案
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧