Foo.tsx
GridControl gridControl2 = new GridControl();
gridControl2.DataSource = exportDataList; // my data list
SaveFileDialog fileDialog = new SaveFileDialog();
fileDialog.Title = "Data Export";
fileDialog.Filter = "Excel (2010) (.xlsx)|*.xlsx|*.xls|*.csv";
DialogResult dialogResult = fileDialog.ShowDialog();
if (dialogResult == DialogResult.OK)
gridControl2.ExportToXls(fileDialog.FileName); // NullReferenceException
Index.tsx
以下代码缺少userId props错误
interface IFooProps{
userId:number
}
class Foo extends Component<IFooProps>{
render(){
return <p>foo...</p>
}
}
const mapStateToProps = (state: IState) => {
return {
userId: state.user.id,
}
}
const mapDispatchToProps = (dispatch: Dispatch) => {
return {}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Foo)
道具来自// missing userId props error
<Foo />
,不需要再次传递。
答案 0 :(得分:4)
您的问题是人们在使用React从JS切换到TS并使用HOC(如connect
及其组件时遇到的常见问题。
基本上,使用您的界面IFooProps
,您告诉TS
每当使用该组件时userId
都是必需的道具且TS
实际上并不知道,userId
将通过connect
照顾/注入道具。
有几种方法可以解决问题,第一种方法是使userId
成为可选属性。
interface IFooProps{
userId?: number
}
这种方法确实有缩小规模,因为当你使用userId
时,TS会要求你执行null / undefined检查。但是,它是最简单的一个。
另一种方法是定义另一个接口,在您的示例中,可以按常规命名:IFooInjectedProps
。您的组件将如下:
interface IFooProps {
// ....Whatever props that require by the component
}
// All injected props from the store
interface IFooInjectedProps extends IFooProps {
userId: number
}
class Foo extends Component<IFooProps> {
get injectedProps() {
// Cast this.props as injectedProps to bypass typechecking
return this.props as IFooInjectedProps;
}
render() {
// Get userId from the getter `injectedProps`
const { userId } = this.injectedProps;
return <p>foo...</p>
}
}
答案 1 :(得分:0)
因为你说过&#34; IFooProps&#34;作为道具的类型,它期望您在使用它时发送userId。将类型设置为any。使用redux时,不需要设置道具的类型。
class Foo extends Component<any>{
render(){
return <p>foo...</p>
}
}
答案 2 :(得分:0)
使用Typescript和Redux几年后,我设法找到了connect
的终极解决方案。这有点棘手
首先,您需要使用typescript中的module augmentation
功能。它允许您扩展现有的类型。我们会用它来扩展react-redux
类型。
您需要使用以下代码在项目中的某个位置创建.ts
文件:
import { Component, ComponentClass } from "react-redux";
declare module "react-redux" {
export interface InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> {
<P extends TInjectedProps>(component: Component<P>): ComponentClass<
Omit<P, keyof TInjectedProps> & TNeedsProps
> & {
WrappedComponent: Component<P>;
};
allProps: TInjectedProps & TNeedsProps;
}
}
这里我们向现有的InferableComponentEnhancerWithProps
接口添加一个属性allProps: TInjectedProps & TNeedsProps;
,其余的代码是从原始类型中重写现有的接口声明。
创建后,typescript编译器会自动选择新的定义,现在我们可以回到你的文件了
<强> Foo.tsx 强>
// you only define props that you would like to pass from the parent component, not the props that are injected from redux state
interface IFooProps {
testProp: any;
}
// IConnectProps are defined at the bottom
class Foo extends Component<IConnectProps>{
render() {
this.props.testProps;
this.props.userId;
return <p>foo...</p>
}
}
const mapStateToProps = (state: IState, props: IFooProps) => {
return {
userId: state.user.id,
...props,
}
}
const mapDispatchToProps = (dispatch: Dispatch) => {
return {}
}
const connectCreator = connect(
mapStateToProps,
mapDispatchToProps
);
// here we use our new typings, thanks to them typescript automatically merge
// props that we declared in mapStateToProps so the IConnectProps
// looks like {
// userId: string;
// testProp: any;
// }
type IConnectProps = typeof connectCreator.allProps;
export default connectCreator(Foo);
<强> Index.tsx 强>
// typescript only tells you to pass a testProp
<Foo testProp={5} />