简体   繁体   English

使用带有自定义钩子的 useContext 与 useContext + useReducer

[英]using useContext with custom hooks vs useContext + useReducer

I recently enterd a project and saw something that I have not seen before when it comes to useContext in react.我最近进入了一个项目,在 react 中的 useContext 时看到了一些我以前没有见过的东西。

The global state in the project uses hooks which it sends in to a context and then when calling that hook later on the global state is accesable.项目中的全局 state 使用它发送到上下文的钩子,然后当稍后在全局 state 上调用该钩子时,它是可访问的。 The problem I saw with this is that ther is no defined global state in one place, you can create a hook with state and update functions, send it in to a provider and get access to it anywhere in the project..我看到的问题是在一个地方没有定义的全局 state,您可以使用 state 创建一个挂钩并更新功能,将其发送给提供商并在项目中的任何位置访问它。

Code:代码:

const initialState = {
  id: "MyId",
  currency: 'currency',
};


function useCurrencyState() {
  initialState.currency = 'newCurrency'
  const [currency, setCurrency] = React.useState(initialState);

  return {
    currency
  };
}


export const [useCurrency, CurrencyStoreProvider] = createStoreProvider(useUserState);

The provider:提供者:

export function createStoreProvider(useHook) {
  const [useContextConsumer, ContextProvider] = generateContext();

  const StoreProvider = ({ children }) => {
    const state = useHook();

    return <ContextProvider value={state}>{children}</ContextProvider>;
  };

  return [useContextConsumer, StoreProvider];
}

generate context func:生成上下文函数:

export function generateContext() {
  const context = React.createContext(undefined);

  const useContextConsumer = () => {
    const c = React.useContext(context);
    if (!c) {
      throw new Error('Component must be wrapped with <Container.Provider>');
    }
    return c;
  };

  return [useContextConsumer, context.Provider];
}

the store:商店:

const StoreProvider = ({ children }) => (
  <CurrencyStoreProvider>
      {children}
  </CurrencyStoreProvider>
);

export default StoreProvider;

and when you want to use the useCurrency you would当你想使用 useCurrency 时,你会

import { useCurrency } from 'store/currency';

 const { currency} = useCurrency ();

The above example is for one hook.上面的例子是一个钩子。 The project has 8 of them which follow the same pattern and the project has 8 nested providers.该项目有 8 个遵循相同的模式,并且该项目有 8 个嵌套提供程序。

My inital thought to this is that it is mutating state anonymously since it doesnt have a global defined state and no reducer who catches the action in order to update the global state.我对此的初步想法是,它正在匿名变异 state,因为它没有全局定义的 state 并且没有减速器捕捉动作以更新全局 Z9ED39E2EA931586B73A985A6942EZEF5。

Am I right?我对吗? Is this the not so recommended way to handle state?这是处理 state 的不那么推荐的方法吗? If im worng what is this pattern called if it has a name?如果我穿了这个图案,如果它有名字,它叫什么?

I was about to recommend to change to using a context + useReducer with action and dispatch but I need to get a better understanding of the above.我正要建议更改为使用带有动作和调度的上下文 + useReducer,但我需要更好地理解上述内容。

Code sample, provided above, has two parts.上面提供的代码示例有两个部分。 One is state management (done with useState , and second is state provider (done with context). Lets discuss them separately.一是 state 管理(使用useState完成,二是 state 提供者(使用上下文完成)。让我们分别讨论。

Generally, useState , useReducer and Redux reducer are all the same.一般来说, useStateuseReducer和 Redux 减速机都是一样的。 They all allow to have some state and render components based on it.它们都允许拥有一些 state 并基于它渲染组件。 They are different in how they allow to manipulate state, especially in complex cases.它们在允许操作 state 的方式上有所不同,尤其是在复杂情况下。

  1. useState is a simplest aspproach. useState是一种最简单的方法。 All you can do is to你所能做的就是
  const [state, setState] = useState()
  setState(/* some new state */)
  // or
  setState(prevState => ({ ...prevState, /* some new state */ }))

It will be hard to add a logic when manipulated a state.操作 state 时很难添加逻辑。 Ie if you want to do currency conversion before calling setCurrency you should do it somewhere, or write custom hook.即,如果您想在调用setCurrency之前进行货币转换,您应该在某处进行,或者编写自定义挂钩。 And this custom hook will be your implementation of Redux action.这个自定义钩子将是您执行 Redux 操作。

It will be even harder to execute async code (fetch currency rates).执行异步代码(获取货币汇率)将更加困难。 Don't forget that fetching rates in useEffect is not enough, as you will have to handle server errors (5xx or 4xx) and show appropriate message to user.不要忘记在useEffect中获取速率是不够的,因为您必须处理服务器错误(5xx 或 4xx)并向用户显示适当的消息。 To store error you'll probably need additional state, or put it inside currency state.要存储错误,您可能需要额外的 state,或将其放入货币 state 中。

Following this approach with complex state will lead you to writing Redux by yourself.使用复杂的 state 遵循这种方法将引导您自己编写 Redux。

  1. useReducer (this is React reducer, not Redux) allows to manipulate complex state with actions. useReducer (这是 React reducer,不是 Redux)允许使用操作来操作复杂的 state。 Ie you'll be able to dispatch SET_CURRENCY action and SET_RATES action separately, and useReducer will update state accordantly.即您将能够分别调度SET_CURRENCY动作和SET_RATES动作,并且useReducer将相应地更新 state。 But it does not have any logic for async code (ie fetching rates from server).但它没有任何异步代码逻辑(即从服务器获取速率)。 You should write it yourself with custom hooks.您应该使用自定义挂钩自己编写。

  2. Redux is most sophisticated approach to handle state. Redux 是处理 state 的最复杂的方法。 It allows to update parts of your state with actions and handle async actions.它允许使用操作更新 state 的部分内容并处理异步操作。 If you'll consider such library as Redux Toolkit , you'll be able to remove a lot of boilerplate code from your project and work with complex state update logic.如果您考虑使用Redux Toolkit之类的库,您将能够从项目中删除大量样板代码并使用复杂的 state 更新逻辑。

From my experience, useState is for simple state, like open dialog.根据我的经验, useState适用于简单的 state,例如打开对话框。 All other state goes to Redux.所有其他 state 转到 Redux。

Additionally, I can mention form's state, which can be manipulated with such library as Reach Hook Forms .另外,我可以提一下表单的 state,它可以使用诸如Reach Hook Forms 之类的库进行操作。 React hook form will hold form specific state internally and provide you with form specific state, like errors, touched, submit count, etc. React 钩子表单将在内部保存特定于表单的 state,并为您提供特定于表单的 state,例如错误、触摸、提交计数等。

Second part in the provided example is state provider part.提供的示例中的第二部分是 state 提供程序部分。 It is done with context.它是通过上下文完成的。 This is expected, as useState does not suggest anything to pass state to components.这是意料之中的,因为useState不建议将 state 传递给组件。 Redux is also using context, but it is created for you by React-Redux library. Redux 也在使用上下文,但它是由 React-Redux 库为您创建的。 Additionally, React-Redux will provide you with useful tools like useSelector to select only parts of the state.此外,React-Redux 将为您提供有用的工具,如useSelector到 select 仅 state 的一部分。 React context does not have any selectors. React 上下文没有任何选择器。 It will give you full state, and you'll have to use useMemo to get part of the state and pass it to lower level components.它将为您提供完整的 state,您必须使用useMemo获取 state 的一部分并将其传递给较低级别的组件。 Again, it is similar to writing React-Redux library yourself.同样,它类似于自己编写 React-Redux 库。

And final question.最后一个问题。 There is no name or pattern for approach, which is used in the provided code.提供的代码中没有方法的名称或模式。 Some developer just invented it for a project.一些开发人员刚刚为一个项目发明了它。 Form my point of view, there is nothing interesting in such approach.从我的角度来看,这种方法没有什么有趣的。

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

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