简体   繁体   English

反应全局 state 没有上下文或 redux?

[英]React global state no context or redux?

I recently game across the following article State Management with React Hooks — No Redux or Context API .我最近玩过以下文章State Management with React Hooks — No Redux 或上下文 API Since reacts inception the most talked about issue is always state management and global state.自 reacts 开始以来,最受关注的问题始终是 state 管理和全局 state。 Redux has been the popular choice and more recently the context API. Redux 一直是流行的选择,最近则是 API。 But this approach seems to be much easier, less code and more scalable.但这种方法似乎更容易,代码更少,可扩展性更强。

My question is can anyone see a down side to using the this type of state management approach that I may have overlooked.我的问题是任何人都可以看到使用我可能忽略的这种类型的 state 管理方法的缺点。 I have tweeked the code a little to support SSR and it works in Nextjs and also made it a little more friendly to use actions and the setting of the state variable.我对代码进行了一些调整以支持 SSR,它可以在 Nextjs 中运行,并且使用操作和 state 变量的设置更加友好。

useGlobalState.js使用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;

Then set up a custom hook for a state variable can be a simple string or a object here is a simple one:然后为 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;

And use in component:并在组件中使用:

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>
  )
}

This all seems like a much cleaner and easier approach and I am wondering why this isn't the go to approach for state management in react.这一切似乎是一种更清洁、更容易的方法,我想知道为什么这不是 go 用于 state 管理的方法。 First you don't have to wrap everything in a provider.首先,您不必将所有内容都包装在提供程序中。 Next it is extremely easy to implement and much less code is involved in the actual app.其次,它非常容易实现,实际应用程序中涉及的代码要少得多。 Can anyone see a downside to using this approach.任何人都可以看到使用这种方法的缺点。 The only thing I can think of is the re rendering issue that the context api has but in small chunks this shouldn't be an issue.我唯一能想到的是上下文 api 具有的重新渲染问题,但在小块中这应该不是问题。

I have been using a similar approach and I really like it.我一直在使用类似的方法,我真的很喜欢它。 I actually can't believe more people don't talk about this approach.我真的不敢相信更多的人不谈论这种方法。 I wrote a custom hook here React Global Store Hook .我在这里写了一个自定义钩子React Global Store Hook It gives you the freedom to dispatch from anywhere in the app and shallow compares to avoid unwanted re-renders.它使您可以自由地从应用程序的任何位置进行调度,并进行浅层比较以避免不必要的重新渲染。 I don't see any performance issues as long as you can avoid the unwanted re-renders.只要您可以避免不需要的重新渲染,我就看不到任何性能问题。

In all it is a simple concept.总而言之,这是一个简单的概念。 You basically create a function to store your state and return 2 functions.您基本上创建了一个 function 来存储您的 state 并返回 2 个函数。 One will be a function to set the stored state and one will be a hook to be used in the react component.一个是 function 来设置存储的 state 另一个是在反应组件中使用的钩子。 In the hook you grab the setState function of react on initial render with a createEffect and store it in an array.在钩子中,您使用 createEffect 获取对初始渲染做出反应的 setState function 并将其存储在数组中。 You can then use this setState function to re render your component.然后,您可以使用此 setState function 重新渲染您的组件。 So when you call the dispatch function you can just loop through these setState functions and call them.因此,当您调用调度 function 时,您可以遍历这些 setState 函数并调用它们。

Simple example:简单的例子:

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]
}

Then just create a store and use in your component然后只需创建一个商店并在您的组件中使用

const [useStore, dispatch] = createStore(0)

const Display = () => {
  const count = useStore()
  return <div>{count}</div>
}

const addToCount = () => 
  <button onClick={ () => dispatch(count => count + 1}>+</button>

Then if you want to avoid re renders you can do a shallow compare in the dispatch function to compare the store to the new store similar to what redux does.然后,如果您想避免重新渲染,您可以在调度 function 中进行浅比较,以将商店与新商店进行比较,类似于 redux 所做的。 Something like the following:类似于以下内容:

const shouldUpdate = (a, b) => {
  for( let key in a ) {
    if(a[key] !== b[key]) return true
  }
  
  return false
}

and then in dispatch you can check this before firing the listener in your forEach loop.然后在调度中,您可以在 forEach 循环中触发侦听器之前检查这一点。

const dispatch = (newStore) => {
  if(!shouldUpdate(
    store, 
    store = typeof newStore === 'function' ? newStore(store) : newstore
  ) return

  listeners.forEach(listener => listener(() => store))
}

Its way less boilerplate than redux and seems to be much cleaner.它的样板比 redux 少得多,而且似乎更干净。 The best thing is it allows you to decouple your actions from functions without attaching the actions to anything.最好的事情是它允许您将操作与功能分离,而无需将操作附加到任何东西。 You can simply create a store anywhere in your app and export the useStore and dispatch functions.您可以简单地在应用程序的任何位置创建商店并导出useStoredispatch函数。 Then you can dispatch from anywhere in your app.然后,您可以从应用程序中的任何位置进行调度。

well good approach but i still see redux better for larger application especially when come to performance.很好的方法,但我仍然认为redux更适合大型应用,尤其是在性能方面。 A example using your approach,is adding The button as separated component while wrapping it with React.memo and firing actions.toggle() from the button component, but the button re render 2 times which it doesn't relay on the changed state.使用您的方法的一个示例是将按钮添加为单独的组件,同时使用React.memo包装它并从按钮组件触发actions.toggle() ,但按钮重新渲染 2 次,它不会在更改后的 state 上中继。

so when building big apps you are always looking for performance improvement by removing unnecessary re renders but this is not the case here.因此,在构建大型应用程序时,您总是希望通过删除不必要的重新渲染来提高性能,但这里并非如此。

this is my analyses, thanks for your work.这是我的分析,感谢您的工作。

here the code showcase这里是代码展示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM