在Redux连接组件中保留类型参数

yvfmudvl  于 2023-10-19  发布在  其他
关注(0)|答案(3)|浏览(119)

我有一个简单的组件,它在props上接受一个类型参数。使用时,它会推断prop类型并根据上下文键入回调参数。但是,当我将组件 Package 在connect()中时,它丢失了类型参数推断,福尔斯返回到unknown。范例:

import React from "react";
import { Component } from "react";
import { connect } from "react-redux"

type MyCompProps<T> = { 
    value: T; 
    onChange(value: T): void;
}

class MyComp<T> extends Component<MyCompProps<T>> { }

<MyComp value={ 25 } onChange={ val => {/* val will be contextually typed to `number` ✅ */} }/>

const stateToProps = (state: any) => ({ });

const dispatchToProps = { };

const MyContainer = connect(stateToProps, dispatchToProps)(MyComp);

<MyContainer value={ 25 } onChange={ val => {/* val is now `unknown` ❌ */} }/>

有没有办法让MyContainerMyComp一样工作?是通过向connect()传递显式类型参数,还是通过在结果类型上编写Assert?

v1l68za4

v1l68za41#

在多次尝试让connect()按照我想要的方式运行失败后,我发现最简单的解决方案是将连接的组件 Package 在另一个组件中,重新暴露类型参数并呈现连接的组件:

const ConnectedMyComp = connect(stateToProps, dispatchToProps)(MyComp);

class MyContainer<T> extends Component<MyCompProps<T>> {
    render() {
        return <ConnectedMyComp { ...this.props }/>;
    }
}

<MyContainer value={ 25 } onChange={ val => {/* val is `number` 👍 */} }/>

完整的例子,也处理了围绕自己的 prop ,状态 prop 和调度 prop 的整个舞蹈:操场

mznpcxlj

mznpcxlj2#

这里有两个不同的答案。
首先,我们在React-Redux文档“Usage with TS”使用指南页面中展示了一种自动推断“来自Redux的所有props的类型”的技术,通过使用ConnectedProps<T>类型并将connect调用分为两部分:

import { connect, ConnectedProps } from 'react-redux'

interface RootState {
  isOn: boolean
}

const mapState = (state: RootState) => ({
  isOn: state.isOn,
})

const mapDispatch = {
  toggleOn: () => ({ type: 'TOGGLE_IS_ON' }),
}

const connector = connect(mapState, mapDispatch)

// The inferred type will look like:
// {isOn: boolean, toggleOn: () => void}
type PropsFromRedux = ConnectedProps<typeof connector>

这在这里可能会有所帮助,尽管我真的不知道connect是否会保留这样的泛型类型。
也就是说,最好的答案是:

使用函数组件和钩子API,而不是类组件和connect

hooks API总体上使用起来更简单,并且与TypeScript配合使用效果更好。此外,由于不再处理高阶组件,因此不会干扰您可能使用的任何通用props类型。

e3bfsja2

e3bfsja23#

我花了很长时间才纠正过来。但我在这里分享它,希望它能对某人有用。

import React from "react";
import { View } from "react-native";
import { connect } from "react-redux";
import * as Redux from "redux";
import { RootAction, RootState } from "../../Reducers";

interface OwnProps<T> {
  options: T[];
  onValueChange?: (value: T, index: number) => void;
  renderItem: (value: T, index: number, isSelected: boolean) => JSX.Element;
  selectedIndex: number;
}

interface StateProps {}

interface DispatchProps {
  dispatch: Redux.Dispatch<RootAction>;
}

type Props<T> = OwnProps<T> & DispatchProps & StateProps;

interface State<T> {
  something: T;
}

export class MyBoxComponent<
  T extends React.ReactNode,
> extends React.PureComponent<Props<T>, State<T>> {
  constructor(props: Props<T>) {
    super(props);
  }

  public render() {
    return <View>{this.props.options}</View>;
  }
}

class MyBox<T extends React.ReactNode> extends React.Component<OwnProps<T>> {
  render() {
    const mapDispatchToProps = (
      dispatch: Redux.Dispatch<Redux.AnyAction>,
    ): DispatchProps => ({
      dispatch,
    });

    const mapStateToProps = (
      state: RootState,
      ownProps: OwnProps<T>,
    ): StateProps => {
      return {};
    };

    const ConnectedComponent = connect(
      mapStateToProps,
      mapDispatchToProps,
    )(MyBoxComponent) as React.ComponentType<OwnProps<T>>;

    return <ConnectedComponent {...this.props} />;
  }
}

export default MyBox;

在另一个文件中:

type MyType = {
    Description: string;
  } & React.ReactNode;

  const renderItem = React.useCallback(
    (value: MyType, index: number, isSelected: boolean) => {
      return (
        <Text style={isSelected ? styles.fontSelected : styles.font}>
          {value.Description}
        </Text>
      );
    },
    [],
  );

  return (
    <MyBox<MyType>
      options={props.listOfMyType}
      renderItem={renderItem}
      onValueChange={(value, index) => {
        // do something
      }}
      selectedIndex={0}
    />
  );

相关问题