根据高阶组件

  • _Enhancers: _Wrap a component with additional functionality/props.
  • Injectors: Inject props into a component.

Enhancers

  1. const withLoading = Component =>
  2. class WithLoading extends React.Component {
  3. render() {
  4. const { loading, ...props } = this.props;
  5. return loading ? <LoadingSpinner /> : <Component {...props} />;
  6. }
  7. };

引入类型

  1. interface WithLoadingProps {
  2. loading: boolean;
  3. }
  4. const withLoading = <P extends object>(Component: React.ComponentType<P>) =>
  5. class WithLoading extends React.Component<P & WithLoadingProps> {
  6. render() {
  7. const { loading, ...props } = this.props;
  8. return loading ? <LoadingSpinner /> : <Component {...props as P} />;
  9. }
  10. };
  1. <P extends object>(Component: React.ComponentType<P>)

这里 P 是泛型,代表传递进组件的属性;React.ComponentTypeReact.FunctionComponent | React.ClassComponent 的别名,意味着传入高阶组件的组件可以是函数组件也可以是类组件。

我们也可以写成函数高阶组件:

  1. const withLoading = <P extends object>(
  2. Component: React.ComponentType<P>
  3. ): React.FC<P & WithLoadingProps> => ({
  4. loading,
  5. ...props
  6. }: WithLoadingProps) =>
  7. loading ? <LoadingSpinner /> : <Component {...props as P} />;

Injectors

一个简单的例子:a HOC that injects a counter value and callbacks to increment and decrement the value:

  1. import { Subtract } from 'utility-types';
  2. export interface InjectedCounterProps {
  3. value: number;
  4. onIncrement(): void;
  5. onDecrement(): void;
  6. }
  7. interface MakeCounterState {
  8. value: number;
  9. }
  10. const makeCounter = <P extends InjectedCounterProps>(
  11. Component: React.ComponentType<P>
  12. ) =>
  13. class MakeCounter extends React.Component<
  14. Subtract<P, InjectedCounterProps>,
  15. MakeCounterState
  16. > {
  17. state: MakeCounterState = {
  18. value: 0,
  19. };
  20. increment = () => {
  21. this.setState(prevState => ({
  22. value: prevState.value + 1,
  23. }));
  24. };
  25. decrement = () => {
  26. this.setState(prevState => ({
  27. value: prevState.value - 1,
  28. }));
  29. };
  30. render() {
  31. return (
  32. <Component
  33. {...this.props as P}
  34. value={this.state.value}
  35. onIncrement={this.increment}
  36. onDecrement={this.decrement}
  37. />
  38. );
  39. }
  40. };
  1. import makeCounter, { InjectedCounterProps } from './makeCounter';
  2. interface CounterProps extends InjectedCounterProps {
  3. style?: React.CSSProperties;
  4. }
  5. const Counter = (props: CounterProps) => (
  6. <div style={props.style}>
  7. <button onClick={props.onDecrement}> - </button>
  8. {props.value}
  9. <button onClick={props.onIncrement}> + </button>
  10. </div>
  11. );
  12. export default makeCounter(Counter);
  1. class MakeCounter extends React.Component<
  2. Subtract<P, InjectedCounterProps>,
  3. MakeCounterState
  4. >

此处我们再次使用泛型P,以确保注入组件的属性。

Enhance + Inject

结合两种模式,我们将创建一个counter,它允许传入最大值和最小值,
we will build on the counter example to allow for the minimum and maximum counter values to be passed into the HOC, which in turn are intercepted and used by it without passing them through to the component:

  1. export interface InjectedCounterProps {
  2. value: number;
  3. onIncrement(): void;
  4. onDecrement(): void;
  5. }
  6. interface MakeCounterProps {
  7. minValue?: number;
  8. maxValue?: number;
  9. }
  10. interface MakeCounterState {
  11. value: number;
  12. }
  13. const makeCounter = <P extends InjectedCounterProps>(
  14. Component: React.ComponentType<P>
  15. ) =>
  16. class MakeCounter extends React.Component<
  17. Subtract<P, InjectedCounterProps> & MakeCounterProps,
  18. MakeCounterState
  19. > {
  20. state: MakeCounterState = {
  21. value: 0,
  22. };
  23. increment = () => {
  24. this.setState(prevState => ({
  25. value:
  26. prevState.value === this.props.maxValue
  27. ? prevState.value
  28. : prevState.value + 1,
  29. }));
  30. };
  31. decrement = () => {
  32. this.setState(prevState => ({
  33. value:
  34. prevState.value === this.props.minValue
  35. ? prevState.value
  36. : prevState.value - 1,
  37. }));
  38. };
  39. render() {
  40. const { minValue, maxValue, ...props } = this.props;
  41. return (
  42. <Component
  43. {...props as P}
  44. value={this.state.value}
  45. onIncrement={this.increment}
  46. onDecrement={this.decrement}
  47. />
  48. );
  49. }
  50. };