根据高阶组件
- _Enhancers: _Wrap a component with additional functionality/props.
- Injectors: Inject props into a component.
Enhancers
const withLoading = Component =>
class WithLoading extends React.Component {
render() {
const { loading, ...props } = this.props;
return loading ? <LoadingSpinner /> : <Component {...props} />;
}
};
引入类型
interface WithLoadingProps {
loading: boolean;
}
const withLoading = <P extends object>(Component: React.ComponentType<P>) =>
class WithLoading extends React.Component<P & WithLoadingProps> {
render() {
const { loading, ...props } = this.props;
return loading ? <LoadingSpinner /> : <Component {...props as P} />;
}
};
<P extends object>(Component: React.ComponentType<P>)
这里 P
是泛型,代表传递进组件的属性;React.ComponentType
是 React.FunctionComponent | React.ClassComponent
的别名,意味着传入高阶组件的组件可以是函数组件也可以是类组件。
我们也可以写成函数高阶组件:
const withLoading = <P extends object>(
Component: React.ComponentType<P>
): React.FC<P & WithLoadingProps> => ({
loading,
...props
}: WithLoadingProps) =>
loading ? <LoadingSpinner /> : <Component {...props as P} />;
Injectors
一个简单的例子:a HOC that injects a counter value and callbacks to increment and decrement the value:
import { Subtract } from 'utility-types';
export interface InjectedCounterProps {
value: number;
onIncrement(): void;
onDecrement(): void;
}
interface MakeCounterState {
value: number;
}
const makeCounter = <P extends InjectedCounterProps>(
Component: React.ComponentType<P>
) =>
class MakeCounter extends React.Component<
Subtract<P, InjectedCounterProps>,
MakeCounterState
> {
state: MakeCounterState = {
value: 0,
};
increment = () => {
this.setState(prevState => ({
value: prevState.value + 1,
}));
};
decrement = () => {
this.setState(prevState => ({
value: prevState.value - 1,
}));
};
render() {
return (
<Component
{...this.props as P}
value={this.state.value}
onIncrement={this.increment}
onDecrement={this.decrement}
/>
);
}
};
import makeCounter, { InjectedCounterProps } from './makeCounter';
interface CounterProps extends InjectedCounterProps {
style?: React.CSSProperties;
}
const Counter = (props: CounterProps) => (
<div style={props.style}>
<button onClick={props.onDecrement}> - </button>
{props.value}
<button onClick={props.onIncrement}> + </button>
</div>
);
export default makeCounter(Counter);
class MakeCounter extends React.Component<
Subtract<P, InjectedCounterProps>,
MakeCounterState
>
此处我们再次使用泛型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:
export interface InjectedCounterProps {
value: number;
onIncrement(): void;
onDecrement(): void;
}
interface MakeCounterProps {
minValue?: number;
maxValue?: number;
}
interface MakeCounterState {
value: number;
}
const makeCounter = <P extends InjectedCounterProps>(
Component: React.ComponentType<P>
) =>
class MakeCounter extends React.Component<
Subtract<P, InjectedCounterProps> & MakeCounterProps,
MakeCounterState
> {
state: MakeCounterState = {
value: 0,
};
increment = () => {
this.setState(prevState => ({
value:
prevState.value === this.props.maxValue
? prevState.value
: prevState.value + 1,
}));
};
decrement = () => {
this.setState(prevState => ({
value:
prevState.value === this.props.minValue
? prevState.value
: prevState.value - 1,
}));
};
render() {
const { minValue, maxValue, ...props } = this.props;
return (
<Component
{...props as P}
value={this.state.value}
onIncrement={this.increment}
onDecrement={this.decrement}
/>
);
}
};