React Context with TypeScript:第四部分 - 创建一个没有默认和未定义检查的上下文

204 阅读3分钟

Photo by mohammad takhsh on Unsplash

这是关于React上下文与TypeScript的系列文章中的最后一篇文章。在上一篇文章中,我们在一个类组件中消耗了一个上下文。在这篇文章中,我们将学习如何在不需要传递默认值的情况下创建一个上下文,然后在消费它的时候做任何undefined 检查:

问题所在

createContext 的类型要求向其传递一个默认值,但通常情况下,传递一个默认值并没有意义。所以,我们最终把undefined 作为默认值:

const ThemeContext = React.createContext<
  ThemeContextType | undefined
>(undefined);

...然后在我们使用它的地方检查undefined

const { theme, setTheme } = useTheme()!;

一个快速的解决方案

这个问题的一个快速解决方案是使用not-null断言操作符,并从上下文类型中删除undefined

const ThemeContext = React.createContext<
  ThemeContextType
>(undefined!);

这样做很好,因为消费代码不需要undefined 检查。然而,我们仍然在传递一个默认值。

创建一个用于创建上下文的包装器

一个解决方案是在createContext 上创建一个包装器,处理默认值和undefined 检查:

export function createCtx<ContextType>() {
  const ctx = React.createContext<
    ContextType | undefined
  >(undefined);
  function useCtx() {
    const c = React.useContext(ctx);
    if (!c)
      throw new Error(
        "useCtx must be inside a Provider with a value"
      );
    return c;
  }
  return [useCtx, ctx.Provider] as const;
}

这个函数首先用传入的通用类型创建上下文,并将undefined 作为其默认值。

然后定义一个嵌套函数,它包裹了useContext 钩子。一个变量c 被分配给useContext 钩子的返回值,即传入的通用类型或undefined

Type of c

然后,如果c 是错误的,我们就抛出一个错误,这就处理了undefined 的检查。这意味着当c 从嵌套函数返回时,它不能undefined ,只能是我们传入的通用类型:

Type of c

注意我们在最后一行使用了一个const断言(as const),以确保TypeScript推断出一个元组类型,而不是一个联合类型的数组。

创建一个上下文

我们现在可以使用我们的createCtx 函数来创建一个上下文,而不是React的createContext

const [useTheme, CtxProvider] = createCtx<
  ThemeContextType
>();

创建一个提供者

我们的createCtx 函数返回一个元组,该元组在第二个元素中包含一个提供者组件 (CtxProvider)。我们可以创建包含我们所需状态的特定提供者组件:

export const ThemeProvider = ({
  children
}: Props) => {
  const [theme, setTheme] = React.useState(
    "white"
  );
  ...
  return (
    <CtxProvider value={{ theme, setTheme }}>      {children}
    </CtxProvider>  );
};

然后,这可以被放置在组件树中的适当位置:

export const App = () => (
  <ThemeProvider>    <Header />
  </ThemeProvider>);

消耗上下文

我们的createCtx 也在图元的第一个元素中返回一个钩子(useTheme)。我们可以使用它而不需要做任何undefined 检查:

const Header = () => {
  const { theme, setTheme } = useTheme();  return (
    <div style={{ backgroundColor: theme }}>
      <select
        value={theme}
        onChange={e =>
          setTheme(e.currentTarget.value)
        }
      >
        <option value="white">White</option>
        <option value="lightblue">Blue</option>
        <option value="lightgreen">Green</option>
      </select>
      <span>Hello!</span>
    </div>
  );
};

很好!

点击下面的链接,可以得到一个完整的工作实现。试一试,改变主题值,看看背景的颜色变化。

打开完整的实现

总结

createCtx 函数是一个通用函数,可以用来为许多情况创建上下文。它简化了消耗的代码,因为不需要对undefined 的检查。

这就是关于React上下文与TypeScript的系列文章的结尾。我希望你喜欢它!