考虑这样的一个场景。我们有一个高阶组件 WrappedComponent
,它接受一个属性类型为 BaseProps
的组件 Component
,然后做以下事情:
WrappedComponent
的属性类型为WrappedComponentProps
- 向其中注入新的属性,属性类型为
InjectedProps
- 将该组件与返回值类型为
IStateProps
的mapStateToProps
、类型为IDispatchProps
的mapDispatchToProps
连接 (connect
) - 在生命周期中添加一些可复用的逻辑
当我们用 JS 的时候,上面的需求很简单:
import React from 'react';
import { connect } from 'react-redux';
import { increaseCount } from './actions';
const withPropsAndConnect = (Component) => {
const injectedProps = {
/* Injected props */
};
class WrappedComponent extends React.Component {
componentDidUpdate() {
/* ....... */
}
render() {
const childProps = {
...injectedProps,
...this.props
};
return <Component {...childProps} />
}
}
const mapStateToProps = (state) => {
return {
count: state.rootReducer.count,
// redux mapStateToProps
};
};
const mapDispatchToProps = {
increase: () => increaseCount()
};
return connect(mapStateToProps, mapDispatchToProps)(WrappedComponent);
}
然而,当我们用 typescript 的时候,这件事就变得十分地麻烦,反正我看着一整页的 typescript 报错,脑子里只有 “ybb”:
经过了一整个晚上的冲浪,终于找到了正确的写法。这里需要借助 utility-types
包的工具泛型 Diff<U, T>
:
import React from 'react';
import { connect } from 'react-redux';
import { Diff } from 'utility-types';
import { increaseCount } from './actions';
import { AppState } from './store/types'; // redux store 状态树类型
export const withPropsAndConnect = <BaseProps extends object>(Component: React.ComponentType<BaseProps>) => {
const mapStateToProps = (state: AppState) => ({
count: state.rootReducer.count,
});
const mapDispatchToProps = {
increase: () => increaseCount()
};
const injectedProps: InjectProps = {
// ...
};
type IStateProps = ReturnType<typeof mapStateToProps>;
type IDispatchProps = typeof mapDispatchToProps;
type ComponentProps = IStateProps & IDispatchProps & WrappedComponentProps;
type ComponentState = WrapperBaseStates;
class WrappedComponent extends React.Component<ComponentProps, ComponentState> {
constructor(props: ComponentProps) {
super(props);
this.state = { /* ... */ };
componentDidUpdate(prevProps: ComponentProps) {
/* ... */
}
render() {
const childProps = {
...injectedProps,
...this.props
};
return (
<Component
{...(injectedProps as BaseProps)}
/>
);
}
}
type HOCProps = Diff<BaseProps, object>;
return connect<IStateProps, IDispatchProps, HOCProps, AppState>
(mapStateToProps, mapDispatchToProps)(WrappedComponent);
}