React Context with TypeScript:第2部分--使用函数组件的复杂上下文

354 阅读3分钟

Photo by mohammad takhsh on Unsplash

这是关于React上下文与TypeScript的系列文章中的第二篇。在上一篇文章中,我们创建了一个简单的上下文,TypeScript能够推断其类型:

在这篇文章中,我们将实现一个更复杂的上下文,我们需要明确指定上下文类型。

明确设置上下文类型

我们将加强上一篇文章中的上下文,以便消费者可以更新主题。

在上一篇文章中,上下文的类型是从默认值推断出来的,也就是一个简单的string 。我们增强的上下文的类型会更复杂一些:

type ThemeContextType = {
  theme: string;
  setTheme: (value: string) => void;
};

因此,将有一个theme 属性,包含主题的当前值和一个setTheme 方法来更新当前主题。

ReactscreateContext 函数希望我们为初始上下文值提供一个参数。我们可以为theme 属性提供一个默认值,但为setTheme 方法提供一个默认实现是没有意义的。所以,一个简单的方法是传入undefined 作为初始值:

const ThemeContext = React.createContext(
  undefined
);

我们来看看上下文值的推断类型是什么。

Inferred context type

如果在严格模式下,上下文值的类型被推断为undefined ,如果不是,则推断为any

所以,ThemeContext 目前还没有按照我们的要求进行类型化。在使用createContext 时,我们怎样才能明确指定上下文的类型呢?嗯,createContext 是一个通用函数。所以,我们可以把上下文值的类型作为一个通用参数传入:

const context = React.createContext<ContextType>(...)

因此,我们可以将我们的上下文类型定为如下:

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

在提供者中使用增强的上下文

在上一篇文章中,对上下文提供者的修改是对我们从它那里提供的值的修改。它不再是一个简单的string ,现在是一个包含theme 属性和setTheme 方法的对象:

export const ThemeProvider = ({
  children
}: Props) => {
  ...

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

Header 组件中添加一个选项来改变主题

useTheme 自定义钩子与上一篇文章中的保持一致。App 组件也保持不变。

但我们要改变Header 组件,以便用户可以改变主题:

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

我们取消了当前的主题值以及useTheme 钩子的setTheme 方法。注意,我们在调用useTheme 钩子后加了一个感叹号(!),以告诉TypeScript编译器,它的返回值不会是undefined

我们还添加了一个下拉选项,可以将主题改为白色蓝色绿色。下拉菜单的值被设置为当前的主题值,当它被改变时,它会调用contextssetTheme 方法来更新共享状态中的内容。

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

打开完整的实现

总结

一般来说,我们需要为上下文创建一个类型,并在创建上下文时明确定义该类型。通常情况下,上下文的初始值是undefined ,所以上下文类型通常是一个包含undefined 的联合类型。在引用上下文时,我们需要使用非空断言操作符(!)来告诉TypeScript它确实有一个值。

在本系列的第四篇文章中,我们将介绍一种不必向上下文传递默认值的方法,并且不必处理它可能是undefined 。在这之前,在下一篇文章中,我们将学习如何用类组件来消费强类型的上下文。