React/TypeScript:通过 HOC 使用上下文

IT技术 reactjs typescript
2021-05-13 19:12:01

我正在尝试使用TypeScript (2.8) 中的 React 文档 (React 16.3) 中的 HOC来实现示例Consuming Context并且失败了。作为参考,React 手册中的代码:

const ThemeContext = React.createContext('light');

// This function takes a component...
export function withTheme(Component) {
  // ...and returns another component...
  return function ThemedComponent(props) {
    // ... and renders the wrapped component with the context theme!
    // Notice that we pass through any additional props as well
    return (
      <ThemeContext.Consumer>
        {theme => <Component {...props} theme={theme} />}
      </ThemeContext.Consumer>
    );
  };
}

我能想到的最好的:

export interface ThemeAwareProps {
  theme: string;
}

const ThemeContext = React.createContext('light');

export function withTheme<P extends ThemeAwareProps, S>(Component: new() => React.Component<P, S>) {
  return function ThemedComponent(props: P) {
    return (
      <ThemeContext.Consumer>
        {theme => <Component {...props} theme={theme} />}
      </ThemeContext.Consumer>
    );
  };
}

class App extends React.Component {

  public render() {
    return (
      <ThemeContext.Provider value={'dark'}>
        <ThemedButton/>
      </ThemeContext.Provider>
    );
  }
}

主题按钮.tsx:

interface ThemedButtonProps extends ThemeAwareProps {
}

interface ThemedButtonState{
}

class ThemedButton extends React.Component<ThemedButtonProps, ThemedButtonState> {

  constructor(props: ThemedButtonProps) {
    super(props);
  }


  public render() {
    return (
      <button className={this.props.theme}/>
    )
  }
}

export default withTheme(ThemedButton);

问题是最后一行 ( export default withTheme(ThemedButton))。TypeScript 编译器抱怨说

type 的typeof ThemedButton参数不能分配给 type 的参数new () => Component<ThemedButtonProps, ThemedButtonState, any>

我错过了什么?

1个回答

大多数情况下你都做对了,只是有一些缺失的部分:

  1. 对于Component,使用React.ComponentType<Props>,它正确地接受类组件和功能组件。我认为new () => ...单独使用在这里不起作用,因为签名不完全匹配。

  2. ThemedButton在使用时排除props,您必须使用一些看起来很神奇的语法:

function ThemedComponent(props: Pick<P, Exclude<keyof P, keyof ThemeAwareProps>>)

这是它的作用:

  • Exclude<keyof P, keyof ThemeAwareProps>意思是“拿到 的钥匙P,然后拿走里面的钥匙ThemeAwareProps
  • Pick<P, ...>然后说,“从P,返回一个只有这些属性的对象类型”

结合这些给我们一个组件,它接受所有的propsThemedButton,减去themeprops,这样我们就可以<ThemedButton />没有错误了。

这是完整的 HOC:

function withTheme<P extends ThemeAwareProps>(Component: React.ComponentType<P>) {
  return function ThemedComponent(props: Pick<P, Exclude<keyof P, keyof ThemeAwareProps>>) {
    return (
      <ThemeContext.Consumer>
        {(theme) => <Component {...props} theme={theme} />}
      </ThemeContext.Consumer>
    )
  }
}

最后,关于这个主题的一篇很好的博客文章,我从中获得了大部分信息。如果您愿意,它还包括一种Pick<...>Omit类型缩短内容的方法


编辑:rest/spread 的行为在 3.2 中发生了变化这个错误作为一个不幸的副作用出现,导致props与其他props合并时的类型被删除。当前的解决方法是转换propsP

    return (
      <ThemeContext.Consumer>
        {(theme) => <Component {...props as P} theme={theme} />}
      </ThemeContext.Consumer>
    )