代码之家  ›  专栏  ›  技术社区  ›  ThunD3eR

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

  •  1
  • ThunD3eR  · 技术社区  · 4 年前

    我最近进入了一个项目,看到了一些以前在react中使用Context时从未见过的东西。

    项目中的全局状态使用它发送到上下文中的钩子,然后在稍后调用该钩子时,全局状态是可访问的。我看到的问题是,在一个地方没有定义的全局状态,你可以创建一个带有状态和更新函数的钩子,将其发送给提供者,并在项目中的任何地方访问它。。

    代码:

    const initialState = {
      id: "MyId",
      currency: 'currency',
    };
    
    
    function useCurrencyState() {
      initialState.currency = 'newCurrency'
      const [currency, setCurrency] = React.useState(initialState);
    
      return {
        currency
      };
    }
    
    
    export const [useCurrency, CurrencyStoreProvider] = createStoreProvider(useCurrencyState);
    

    供应商:

    export function createStoreProvider(useHook) {
      const [useContextConsumer, ContextProvider] = generateContext();
    
      const StoreProvider = ({ children }) => {
        const state = useHook();
    
        return <ContextProvider value={state}>{children}</ContextProvider>;
      };
    
      return [useContextConsumer, StoreProvider];
    }
    

    生成上下文函数:

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

    商店:

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

    当你想使用useCurrency时

    import { useCurrency } from 'store/currency';
    
     const { currency} = useCurrency ();
    

    上面的例子是一个钩子。该项目有4个遵循相同模式的提供者/上下文,该项目有4个嵌套的提供者/上下文。

    我最初的想法是,它是匿名变异状态,因为它没有全局定义的状态,也没有捕获操作以更新全局状态的还原者。

    我说得对吗?这是不是不推荐的处理状态的方法?如果我在工作,如果这个模式有名字,它叫什么?

    我打算建议改为使用带有操作和分派的context+useReducer,但我需要更好地理解上述内容。

    编辑:

    为清晰起见:校准仪的嵌套方式如下:

    const StoreProvider = ({ children }) => (
    <CurrencyProvider>
      <UserStoreProvider>
        <CartStoreProvider>
          <NotificationsStoreProvider>
            {children}
          </NotificationsStoreProvider>
        </CartStoreProvider>
      </UserStoreProvider>
    </CurrencyProvider>
    );
    

    由于我对这种方法持怀疑态度,使用一个上下文和useReducer/Redux来管理状态更新会更有效吗?。

    我猜上面的例子(编辑部分)是为了防止在状态更新时重新加载,如果不使用减速机,这可能是有意义的。

    0 回复  |  直到 4 年前
        1
  •  2
  •   Fyodor Yemelyanenko    4 年前

    上面提供的代码示例包含两部分。一个是国家管理(通过 useState 第二个是状态提供程序(通过上下文完成)。让我们分开讨论。

    一般来说, useState , useReducer Redux和Redux都是一样的。它们都允许有一些状态,并基于状态渲染组件。它们在允许操纵状态的方式上有所不同,尤其是在复杂的情况下。

    1. useState 这是一种最简单的方法。你所能做的就是
      const [state, setState] = useState()
      setState(/* some new state */)
      // or
      setState(prevState => ({ ...prevState, /* some new state */ }))
    

    当操纵一个状态时,很难添加逻辑。也就是说,如果你想在打电话之前进行货币兑换 setCurrency 你应该在某个地方做,或者写自定义钩子。这个定制钩子将是您的Redux操作的实现。

    执行异步代码(获取货币汇率)将更加困难。别忘了把价格收进来 useEffect 这还不够,因为您必须处理服务器错误(5xx或4xx),并向用户显示适当的消息。要存储错误,您可能需要额外的状态,或者将其放入货币状态。

    在复杂状态下遵循这种方法将导致您自己编写Redux。

    1. 用户教育者 (这是React reducer,而不是Redux)允许通过操作操纵复杂状态。也就是说,你可以 SET_CURRENCY 行动和 SET_RATES 单独行动,以及 用户教育者 将相应地更新状态。但它没有任何异步代码逻辑(即从服务器获取速率)。你应该用定制的钩子自己写。

    2. Redux是处理状态最复杂的方法。它允许使用操作更新部分状态,并处理异步操作。如果你考虑这样的图书馆 Redux Toolkit ,您将能够从项目中删除大量样板代码,并使用复杂的状态更新逻辑。

    根据我的经验, useState 用于简单状态,如打开对话框。其他州都去了Redux。

    此外,我可以提到表单的状态,它可以通过以下库进行操作: Reach Hook Forms .React hook表单将在内部保存特定于表单的状态,并为您提供特定于表单的状态,如错误、触碰、提交计数等。

    提供的示例中的第二部分是状态提供者部分。它是根据上下文完成的。正如预期的那样 useState 不建议将状态传递给组件。Redux也使用上下文,但它是由React Redux库为您创建的。此外,React Redux将为您提供有用的工具,如 useSelector 只选择州的一部分。React上下文没有任何选择器。它会给你完整的状态,你必须使用 useMemo 获取部分状态并将其传递给较低级别的组件。同样,这类似于自己编写React Redux库。

    最后一个问题。在提供的代码中没有使用的方法的名称或模式。一些开发者只是为了一个项目而发明的。在我看来,这种方法没有什么有趣的地方。

    推荐文章