![](/img/trans.png)
[英]ReactJS Redux State (global state) + React.Context (local state)
[英]React global state no context or redux?
我最近玩过以下文章State Management with React Hooks — No Redux 或上下文 API 。 自 reacts 开始以来,最受关注的问题始终是 state 管理和全局 state。 Redux 一直是流行的选择,最近则是 API。 但这种方法似乎更容易,代码更少,可扩展性更强。
我的问题是任何人都可以看到使用我可能忽略的这种类型的 state 管理方法的缺点。 我对代码进行了一些调整以支持 SSR,它可以在 Nextjs 中运行,并且使用操作和 state 变量的设置更加友好。
使用GlobalState.js
import React, { useState, useEffect, useLayoutEffect } from 'react';
const effect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
function setState(newState) {
if (newState === this.state) return;
this.state = newState;
this.listeners.forEach((listener) => {
listener(this.state);
});
}
function useCustom() {
const newListener = useState()[1];
effect(() => {
this.listeners.push(newListener);
return () => {
this.listeners = this.listeners.filter((listener) => listener !== newListener);
};
}, []);
return [this.state, this.setState, this.actions];
}
function associateActions(store, actions) {
const associatedActions = {};
if (actions) {
Object.keys(actions).forEach((key) => {
if (typeof actions[key] === 'function') {
associatedActions[key] = actions[key].bind(null, store);
}
if (typeof actions[key] === 'object') {
associatedActions[key] = associateActions(store, actions[key]);
}
});
}
return associatedActions;
}
const useGlobalHook = (initialState, actions) => {
const store = { state: initialState, listeners: [] };
store.setState = setState.bind(store);
store.actions = associateActions(store, actions);
return useCustom.bind(store, React);
};
export default useGlobalHook;
然后为 state 变量设置一个自定义挂钩,该变量可以是一个简单的字符串,也可以是一个 object,这是一个简单的:
import useGlobalState from './useGlobalState';
const initialState = 'Hi';
// Example action for complex processes setState will be passed to component for use as well
const someAction = (store, val) => store.setState(val);
const useValue = useGlobalState(initialState, { someAction });
export default useValue;
并在组件中使用:
import React from 'react'
import useVal from './useVal'
export default () => {
const [val, setVal, actions] = useVal();
const handleClick = () => {
setVal('New Val');
// or use some actions
actions.someAction('New Val');
}
return(
<div>{val}</div>
<button onClick={handleClick}>Click Me</button>
)
}
这一切似乎是一种更清洁、更容易的方法,我想知道为什么这不是 go 用于 state 管理的方法。 首先,您不必将所有内容都包装在提供程序中。 其次,它非常容易实现,实际应用程序中涉及的代码要少得多。 任何人都可以看到使用这种方法的缺点。 我唯一能想到的是上下文 api 具有的重新渲染问题,但在小块中这应该不是问题。
我一直在使用类似的方法,我真的很喜欢它。 我真的不敢相信更多的人不谈论这种方法。 我在这里写了一个自定义钩子React Global Store Hook 。 它使您可以自由地从应用程序的任何位置进行调度,并进行浅层比较以避免不必要的重新渲染。 只要您可以避免不需要的重新渲染,我就看不到任何性能问题。
总而言之,这是一个简单的概念。 您基本上创建了一个 function 来存储您的 state 并返回 2 个函数。 一个是 function 来设置存储的 state 另一个是在反应组件中使用的钩子。 在钩子中,您使用 createEffect 获取对初始渲染做出反应的 setState function 并将其存储在数组中。 然后,您可以使用此 setState function 重新渲染您的组件。 因此,当您调用调度 function 时,您可以遍历这些 setState 函数并调用它们。
简单的例子:
import { useState, useEffect } from 'react'
const createStore = (initialStore) => {
let store = initialStore
const listeners = new Set()
const dispatch = (newStore) => {
// Make it like reacts setState so if you pass in a function you can get the store value first
store = typeof newStore === 'function' ? newStore(store) : newStore
listeners.forEach(listener => listener(() => store))
}
const useStore = () => {
const [, listener] = useState()
useEffect(() => {
listeners.add(listener)
return () => listeners.delete(listener)
}, [])
return store
}
return [useStore, dispatch]
}
然后只需创建一个商店并在您的组件中使用
const [useStore, dispatch] = createStore(0)
const Display = () => {
const count = useStore()
return <div>{count}</div>
}
const addToCount = () =>
<button onClick={ () => dispatch(count => count + 1}>+</button>
然后,如果您想避免重新渲染,您可以在调度 function 中进行浅比较,以将商店与新商店进行比较,类似于 redux 所做的。 类似于以下内容:
const shouldUpdate = (a, b) => {
for( let key in a ) {
if(a[key] !== b[key]) return true
}
return false
}
然后在调度中,您可以在 forEach 循环中触发侦听器之前检查这一点。
const dispatch = (newStore) => {
if(!shouldUpdate(
store,
store = typeof newStore === 'function' ? newStore(store) : newstore
) return
listeners.forEach(listener => listener(() => store))
}
它的样板比 redux 少得多,而且似乎更干净。 最好的事情是它允许您将操作与功能分离,而无需将操作附加到任何东西。 您可以简单地在应用程序的任何位置创建商店并导出useStore
和dispatch
函数。 然后,您可以从应用程序中的任何位置进行调度。
很好的方法,但我仍然认为redux
更适合大型应用,尤其是在性能方面。 使用您的方法的一个示例是将按钮添加为单独的组件,同时使用React.memo
包装它并从按钮组件触发actions.toggle()
,但按钮重新渲染 2 次,它不会在更改后的 state 上中继。
因此,在构建大型应用程序时,您总是希望通过删除不必要的重新渲染来提高性能,但这里并非如此。
这是我的分析,感谢您的工作。
这里是代码展示
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.