如何在TypeScript中正确使用withRouter、connect、React.Component及自定义属性?
我来帮你把这类带connect、withRouter和自定义属性的React组件的TypeScript正确实现理清楚,结合你遇到的TS2339问题,咱们一步步拆解验证你的实现是否靠谱:
正确实现带Redux & Router的React TypeScript组件
这类组件的类型需要覆盖三个核心部分:自定义传入属性、withRouter注入的路由属性、connect注入的Redux状态/方法属性。我们一步步构建符合规范的实现:
1. 先定义所有属性的类型
首先明确你的组件需要接收的所有属性类别:
- 自定义属性:比如你示例里的
className,单独抽成一个接口:
interface MyTestCustomProps { className?: string; // 其他你需要的自定义属性都放在这里 }
- 路由属性:不用自己写,直接用
react-router-dom提供的RouteComponentProps,它包含了history、location、match这些路由相关属性:
import { RouteComponentProps } from 'react-router-dom';
- Redux注入属性:如果你的组件通过
connect获取Redux的状态或dispatch方法,也要定义对应的类型:
// 假设你有用户状态和一个获取数据的action interface MyTestReduxProps { currentUser: { name: string; id: number }; fetchUserData: () => void; }
然后把这三个类型合并成组件的完整属性类型:
type MyTestProps = MyTestCustomProps & RouteComponentProps & MyTestReduxProps;
2. 编写组件本体
组件直接接收合并后的MyTestProps即可,不用额外处理路由或Redux的注入逻辑,因为withRouter和connect会自动把这些属性传进来:
const MyTestComponent: React.FC<MyTestProps> = ({ className, history, currentUser, fetchUserData }) => { // 组件逻辑示例 return ( <div className={className}> <p>当前页面路径: {history.location.pathname}</p> <p>当前用户: {currentUser.name}</p> <button onClick={fetchUserData}>刷新用户数据</button> </div> ); };
3. 正确包裹connect和withRouter
这里有两个关键注意点:
- 包裹顺序:必须用
withRouter包裹connect的结果,因为connect返回的组件需要withRouter注入路由属性,反过来会导致路由属性被connect过滤掉。 - connect的泛型参数:一定要给
connect指定泛型,明确注入的Redux属性和组件的非Redux属性,避免TypeScript推断错误。
示例代码:
import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; // 假设你的Redux根状态类型是RootState const mapStateToProps = (state: RootState) => ({ currentUser: state.user, }); const mapDispatchToProps = { fetchUserData, }; // connect的泛型参数依次是:Redux注入的props, 组件接收的非Redux props, Redux根状态类型, 额外dispatch参数 const ConnectedMyTest = connect<MyTestReduxProps, {}, MyTestCustomProps & RouteComponentProps, RootState>( mapStateToProps, mapDispatchToProps )(MyTestComponent); // withRouter不需要额外泛型,它会自动推断组件的属性类型 const MyTest = withRouter(ConnectedMyTest); export default MyTest;
4. 验证你的实现是否正确
你之前遇到的TS2339错误,正是因为没有把RouteComponentProps合并到组件属性里,导致TypeScript不知道history这类路由属性的存在。现在按照上面的方式定义后,TypeScript能正确识别所有注入的属性,自然不会报错。
而你使用<MyTest className={"active"} />的方式是完全正确的,className属于我们定义的MyTestCustomProps,TypeScript会自动校验这个属性的类型是否符合要求。
常见坑点提醒
- 别搞反
connect和withRouter的包裹顺序,否则路由属性无法正常注入。 - 不要省略
connect的泛型参数,否则TypeScript无法正确推断Redux注入的属性类型,容易出现隐性错误。 - 一定要把
RouteComponentProps合并到组件的属性类型里,不然用到路由相关属性时就会触发TS2339错误。
内容的提问来源于stack exchange,提问作者chmielot
相关产品推荐
相关产品推荐

