【问题标题】:React.createContext point of defaultValue?React.createContext指向defaultValue?
【发布时间】:2022-03-12 18:40:17
【问题描述】:

React 16 Context doc page 上,他们有与此类似的示例:

const defaultValue = 'light'
const SomeContext = React.createContext(defaultValue)

const startingValue = 'light'
const App = () => (
  <SomeContext.Provider theme={startingValue}>
    Content
  </SomeContext.Provider>
)

似乎defaultValue 没有用,因为如果您将startingValue 设置为其他任何值或不设置它(即undefined),它会覆盖它。没关系,它应该这样做。

那么defaultValue 的意义何在?

如果我想拥有一个不会改变的静态上下文,那么能够执行以下操作会很不错,并且只需通过 defaultValue 传递 Provider 即可

const App = () => (
  <SomeContext.Provider>
    Content
  </SomeContext.Provider>
)

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    当没有提供者时,defaultValue 参数用于函数createContext。这有助于在不包装组件的情况下单独测试组件,或使用来自 Provider 的不同值对其进行测试。


    代码示例:

    import { createContext, useContext } from "react";
    
    const Context = createContext( "Default Value" );
    
    function Child() {
      const context = useContext(Context);
      return <h2>Child1: {context}</h2>;
    }
    
    function Child2() {
      const context = useContext(Context);
      return <h2>Child2: {context}</h2>;
    }
    
    function App() {
    
      return (
        <>
          <Context.Provider value={ "Initial Value" }>
            <Child /> {/* Child inside Provider will get "Initial Value" */}
          </Context.Provider>
            <Child2 /> {/* Child outside Provider will get "Default Value" */}
        </>
      );
    }
    

    Codesandbox Demo

    【讨论】:

    • 来自reactjs.org/docs/context.html#reactcreatecontext:“defaultValue 参数仅在组件在树中没有匹配的 Provider 时使用。这有助于在不包装组件的情况下单独测试组件。注意:将 undefined 作为 Provider 值传递不会导致消费组件使用 defaultValue。"
    • 如果你想在你的应用程序中创建一个单例,只公开上下文的提供者是一种好习惯吗(它总是返回默认值)?
    • 所以这又是一次黑客攻击。
    • 对此很生气...所以根据提供者的设置是否正确,使用了两个默认值。说提供者没有正确设置你将得到'默认'而不是初始值,就像为什么它错了
    【解决方案2】:

    只是分享我在使用 TypeScript 时的典型设置,以完成上面@tiomno 的回答,因为我认为许多最终来到这里的谷歌人实际上都在寻找这个:

    interface GridItemContextType {
        /** Unique id of the item */
        i: string;
    }
    const GridItemContext = React.createContext<GridItemContextType | undefined>(
        undefined
    );
    
    export const useGridItemContext = () => {
        const gridItemContext = useContext(GridItemContext);
        if (!gridItemContext)
            throw new Error(
                'No GridItemContext.Provider found when calling useGridItemContext.'
            );
        return gridItemContext;
    };
    

    钩子在这种情况下提供了更安全的输入。 undefineddefaultValue 可防止您忘记设置提供程序。

    【讨论】:

    • 我认为这是在 TypeScript 中做事的最佳方式,假设没有有意义的 defaultValue 可提供。尽管 Eric 只是一个注释 - 你不应该在最后使用 as GridItemContextType 进行转换,因为 TypeScript 应该已经能够通过上面的条件来缩小 gridItemCheck 的类型。
    • 是的,已经编辑了
    • ? 我一直在寻找一种实际上不必这样做的方法,但我想我必须这样做。最好的部分是,以后使用 useGridItemContext 时无需环绕 if(){}
    • 你可以创建一个泛型类型的辅助函数,如果你真的有很多上下文,它会返回上下文和访问器钩子,并带有未定义的检查。由于冗长,我也讨厌设置新的上下文:)
    • 这就是方式
    【解决方案3】:

    我的两分钱:

    像往常一样阅读 Kent C. Dodds 的this instructive article 后:),我了解到当你解构 useContext 返回的值时,defaultValue 很有用:

    在代码库的一角定义上下文,不使用 defaultValue:

    const CountStateContext = React.createContext() // <-- define the context in one corner of the codebase without defaultValue
    

    并在组件中像这样使用它:

    const { count } = React.useContext(CountStateContext)
    

    JS 显然会说TypeError: Cannot read property 'count' of undefined

    但你不能这样做,完全避免使用 defaultValue。

    关于测试,我的肯特老师说得很好:

    React 文档建议提供默认值“可能会有所帮助 在不包装组件的情况下单独测试组件。”虽然它是 确实它允许你这样做,我不同意它比 用必要的上下文包装你的组件。请记住 每次你在你的测试中做了一些你在你的测试中没有做的事情 应用程序,您降低了测试可以提供的置信度 你。

    TypeScript 的额外功能;如果您不想使用 defaultValue,可以通过以下操作轻松取悦 lint:

    const MyFancyContext = React.createContext<MyFancyType | undefined>(undefined)
    

    您只需要确保稍后添加额外的验证,以确保您已经涵盖了 MyFancyContext === undefined 时的情况

    • MyFancyContext ?? '默认'
    • MyFancyContext?.notThatFancyProperty

    【讨论】:

    • 您链接到的文章说defaultValue 在您没有将消费者放在适当的提供者中时使用。返回值是否被解构无所谓,只是一个例子
    • 谢谢你!不久前,我阅读了 Dan Abramov 对此的评论(从 d.ts 文件链接的评论:github.com/DefinitelyTyped/DefinitelyTyped/pull/…),但它从来没有为我“点击”过。但我只是遵循了指令,因为我有什么资格不同意对 React 和上下文了解一两件事的人呢?你对 KCD 的引用(~“像你运行一样测试”)让我意识到 defaultValue 有什么问题:它允许“坏”开发人员渲染依赖于上下文而不提供组件的组件,同时惩罚那些“好”开发人员正确构建他们的应用程序。
    • 您可以为上下文创建自定义挂钩并强制转换:const useMyContext = () =&gt; { const context = useContext(MyContext); if (!context) throw new Error("MyContext Provider not found"); return context as MyContextType; }。这样在下游组件中,可以保证定义值(需要较少的检查)
    【解决方案4】:

    您可以使用 useReducer 钩子设置默认值,然后第二个参数将是默认值:

    import React, { createContext, useReducer } from "react";
    import { yourReducer } from "./yourReducer";
    
    export const WidgetContext = createContext();
    
    const ContextProvider = (props) => {
    
      const { children , defaultValues } = props;
     
      const [state, dispatch] = useReducer(yourReducer, defaultValues);
    
      return (
        <WidgetContext.Provider value={{ state, dispatch }}>
          {children}
        </WidgetContext.Provider>
      );
    };
    
    export default ContextProvider;
    

    // 实现

       <ContextProvider
                      defaultValues={{
                        disabled: false,
                        icon: undefined,
                        text: "Hello",
                        badge: "100k",
                        styletype: "primary",
                        dir: "ltr",
                        }}
                    >
        </ContextProvider>
        
    

    【讨论】:

      猜你喜欢
      • 2019-07-21
      • 1970-01-01
      • 2020-02-18
      • 2012-06-10
      • 1970-01-01
      • 1970-01-01
      • 2011-01-20
      • 2022-11-04
      • 2012-02-28
      相关资源
      最近更新 更多