使用 TypeScript 和 React 键入 redux 表单 v7
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/46078452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Typing redux forms v7 with TypeScript and React
提问by Kyle Truong
I've got a plain react-redux-powered form. I wish for there to be a form.container.tsx and a form.component.tsx, where form.container.tsx holds all the connections to redux state minus the Field's. I'm trying to wrap my container in react-redux's connect and then wrapping reduxForm within it to look something like TypeScript, redux-form and connect:
我有一个简单的 react-redux 驱动的表单。我希望有一个 form.container.tsx 和一个 form.component.tsx,其中 form.container.tsx 保存所有与 redux 状态的连接减去 Field 的连接。我试图将我的容器包装在 react-redux 的 connect 中,然后将 reduxForm 包装在其中,使其看起来像TypeScript、redux-form 和 connect:
(ideal) form.container.tsx:
(理想)form.container.tsx:
interface DummyFormContainerProps {}
export const DummyFormContainer: React.SFC<DummyFormContainerProps> = props => {
const submitForm = (formValues: object) => {
alert(formValues);
};
return (
<DummyForm
onSubmit={submitForm}
/>
);
};
const mapStateToProps = (state: State) => ({});
const mapDispatchToProps = (dispatch: object) => {
return {};
};
const mergeProps = (stateProps: State, dispatchProps: object | null, ownProps: object | void) =>
Object.assign({}, stateProps, dispatchProps, ownProps);
const formConfiguration = {
form: 'dummy-form',
forceUnregisterOnUnmount: true
};
export default connect(mapStateToProps, mapDispatchToProps)(
reduxForm(formConfiguration)(DummyFormContainer)
);
The above does not work, but if I take out the reduxForm() part, I'm left with a working container with no reduxForm Integration:
以上不起作用,但如果我取出 reduxForm() 部分,我会留下一个没有 reduxForm 集成的工作容器:
(working without reduxForm) form.container.tsx:
(在没有 reduxForm 的情况下工作)form.container.tsx:
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
DummyFormContainer
);
And I've tried different variations with reduxForms and connect, all not currently working:
我已经尝试了 reduxForms 和 connect 的不同变体,但目前都不起作用:
(with classes) form.container.tsx:
(带类)form.container.tsx:
export class DummyFormContainer extends React.Component<DummyFormContainerProps, void> {
submitForm = (formValues: object) => {
alert(formValues);
}
render() {
return (
<DummyForm
onSubmit={this.submitForm}
/>
);
}
}
const mapStateToProps = (state: State) => ({});
const mapDispatchToProps = (dispatch: object) => {
return {};
};
const mergeProps = (stateProps: State, dispatchProps: object | null, ownProps: object | void) =>
Object.assign({}, stateProps, dispatchProps, ownProps);
const formConfiguration = {
form: 'business-registration',
};
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
reduxForm(formConfiguration)(DummyFormContainer) // ERROR
);
error:
错误:
./src/modules/dummy-form/dummy-form.container.tsx
(100,32): error TS2345: Argument of type 'typeof DummyFormContainer' is not assignable to parameter of type 'ComponentType<InjectedFormProps<{}, {}>>'.
Type 'typeof DummyFormContainer' is not assignable to type 'StatelessComponent<InjectedFormProps<{}, {}>>'.
Type 'typeof DummyFormContainer' provides no match for the signature '(props: InjectedFormProps<{}, {}> & { children?: ReactNode; }, context?: any): ReactElement<any> | null'.
(with stateless functional components) form.container.tsx:
(带有无状态功能组件)form.container.tsx:
export const DummyFormContainer: React.SFC<DummyFormContainerProps> = props => {
const submitForm = (formValues: object) => {
alert(formValues);
};
return (
<DummyForm
onSubmit={submitForm}
/>
);
};
export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(
reduxForm(formConfiguration)(DummyFormContainer) // ERROR
);
error:
错误:
./src/modules/dummy-form/dummy-form.container.tsx
(100,3): error TS2345: Argument of type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' is not assignable to parameter of type 'ComponentType<(State & null & void) | (State & null & object) | (State & object & void) | (State ...'.
Type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' is not assignable to type 'StatelessComponent<(State & null & void) | (State & null & object) | (State & object & void) | (S...'.
Type 'DecoratedComponentClass<{}, Partial<ConfigProps<{}, {}>>>' provides no match for the signature '(props: (State & null & void & { children?: ReactNode; }) | (State & null & object & { children?: ReactNode; }) | (State & object & void & { children?: ReactNode; }) | (State & object & { children?: ReactNode; }), context?: any): ReactElement<any> | null'.
The form.component.tsx looks like this:
form.component.tsx 看起来像这样:
import * as React from 'react';
import Input from '../../components/input';
interface DummyFormProps {
onSubmit: (formValues: object) => void
}
export const DummyForm: React.SFC<DummyFormProps> = () => {
return (
<div>
<h1>DummyForm (no state)</h1>
<form>
<Input inputType="primary" />
</form>
</div>
);
};
export default DummyForm;
And the < Input > component is a regular React component.
而 <Input> 组件是一个常规的 React 组件。
Does anyone know how to properly connect reduxForm and react-redux's connect()?
有谁知道如何正确连接 reduxForm 和 react-redux 的 connect()?
采纳答案by bel
I also ran into this issue trying to initialise my form from redux state, as per the example in https://redux-form.com/7.0.4/examples/initializefromstate/
我也遇到了这个问题,试图从 redux 状态初始化我的表单,如https://redux-form.com/7.0.4/examples/initializefromstate/ 中的示例
I ended up getting around it by connecting the component at a higher level, eg:
我最终通过在更高级别连接组件来解决它,例如:
component.tsx:
组件.tsx:
interface DummyFormComponentProps {} extends InjectedFormProps
const DummyFormComponent: React.SFC<DummyFormComponentProps> = props => {
return (
<form onSubmit={props.handleSubmit}>
// Fields go here
</form>
)
}
export const DummyForm = reduxForm({
form: "dummy-form"
})(DummyFormComponent)
// Trying to connect here also gave errors with DecoratedComponentClass
container.tsx:
容器.tsx:
interface DummyFormContainerProps {} extends Pick<InjectedFormProps,
"initialValues"
>
const submitForm = (formValues: object) => {
alert(formValues);
};
const DummyFormContainer: React.SFC<DummyFormContainerProps> = props => {
return (
<DummyForm
initialValues={props.initialValues}
onSubmit={submitForm}
/>
)
}
const mapStateToProps = (state: State) => ({
initialValues: {}
});
const mapDispatchToProps = (dispatch: object) => {
return {};
};
export default connect(mapStateToProps, mapDispatchToProps)(DummyFormContainer)
回答by Timofey Sergeyev
Here's a fully typed example that allows initializing a form using initialValues
and passing additional props (as IOwnProps
):
这是一个完全类型化的示例,它允许使用initialValues
和传递额外的 props (as IOwnProps
)来初始化表单:
sampleForm.tsx:
样本表格.tsx:
export interface IFormData {
userId: string;
}
export interface IOwnProps {
foo: string;
}
export interface IDispatchProps {
onSubmit: (data: IFormData, dispatch: Dispatch<any>, props: IOwnProps) => void;
}
type AllSampleFormProps = IOwnProps & IDispatchProps & InjectedFormProps<IFormData, IOwnProps>;
const SampleForm: React.SFC<AllSampleFormProps> = (props) => (
<form onSubmit={props.handleSubmit(props.onSubmit)}>
<div>foo={props.foo}</div>
<Field name="userId" component="input" />
<button type="submit">Submit</button>
</form>
);
export const DecoratedSampleForm = reduxForm<IFormData, IOwnProps>({})(SampleForm);
sampleForm.ts:
样本表格.ts:
The trick here is to specify proper return type for mapStateToProps
, otherwise compiler will be complaining like other authors pointed out.
这里的技巧是为 指定正确的返回类型mapStateToProps
,否则编译器会像其他作者指出的那样抱怨。
function mapStateToProps(state: AppState, props: IOwnProps): ConfigProps<IFormData, IOwnProps> {
return {
form: "sampleForm", // Form will be handled by Redux Form using this key
initialValues: {
userId: state.somethere.userId // Can also be calculated using props
}
}
}
function mapDispatchToProps(dispatch: Dispatch<any>): IDispatchProps {
return {
onSubmit: (formData: IFormData, dispatch: Dispatch<any>, props: IOwnProps) => {
console.log(formData);
console.log(props);
}
}
}
export default connect<ConfigProps<IFormData, IOwnProps>>(
mapStateToProps,
mapDispatchToProps
)(DecoratedSampleForm);
Now this form can be mounted like this:
现在这个表单可以像这样安装:
<FormContainer foo="bar"/>
回答by Tom McKinney
I found that I was able to dismiss the error by providing the connect statement with empty TStateProps
and TDispatchProps
objects.
我发现我可以通过提供带有空TStateProps
和TDispatchProps
对象的连接语句来消除错误。
interface SampleFormData {
username: string;
}
interface SampleFormProps {
saveData: (data: SampleFormData) => void;
}
type AllSampleFormProps = SampleFormProps & InjectedFormProps<SampleFormData>;
const SampleForm: React.SFC<AllSampleFormProps> = (props) => (
<form onSubmit={props.handleSubmit(props.saveData)}>
<Field name="username" component="input" />
</form>
);
const DecoratedSampleForm = reduxForm<SampleFormData>({ form: "sampleForm" })(SampleForm);
export default connect<{},{}>(
() => ({}),
(dispatch) => ({
saveData: (data: SampleFormData) => dispatch({ type: "SAVE_DATA", data })
})
)(DecoratedSampleForm);
The one downside to this is that it forces us to blindly supply connect props but I felt that this was a more elegant solution than writing an override @types declaration.
这样做的一个缺点是它迫使我们盲目地提供连接道具,但我觉得这是一个比编写覆盖 @types 声明更优雅的解决方案。
To address this shortcoming, I was able to validate the types by providing connect with the correct interfaces versus empty objects; however, this method can only be done temporarily to check the bindings as it doesn't resolve the DecoratedComponentClass
error.
为了解决这个缺点,我能够通过提供连接正确的接口而不是空对象来验证类型;但是,此方法只能临时用于检查绑定,因为它不能解决DecoratedComponentClass
错误。
export default connect<{}, SampleFormProps, InjectedFormProps<SampleFormData>>(
() => ({}),
(dispatch) => ({
saveData: (data: SampleFormData) => dispatch({ type: "SAVE_DATA", data })
})
)(DecoratedSampleForm);
回答by Kyle Truong
What we ended up doing was to close our eyes and override the default types with a type declaration file:
我们最终做的是闭上眼睛并使用类型声明文件覆盖默认类型:
redux-forms.d.ts:
redux-forms.d.ts:
declare module 'redux-form' {
type anyProps = { [key: string]: {} }
function Field(): React.Component<anyProps>;
function reduxForm({}): <T>(c: T) => T
function reducer(): object
interface SubmissionError {
new(error?: {}) : Error;
}
function getFormValues(formName: string): (formName: {}) => {}
function stopSubmit(formName: string, errorObject?: {}): any
function isSubmitting(formName: string): any
function setSubmitFailed(formName: string): any
function setSubmitSucceeded(formName: string): any
function touch(formName: string): any
function clearSubmitErrors(formName: string): any
function getFormMeta(formName: string, ...fields: string[]): (state: {}) => {}
function getFormSyncErrors(formName: string): (state: {}) => {}
function getFormSubmitErrors(formName: string): (state: {}) => {}
function getFormNames(): any
}
回答by TimArsen
I had the same problem and found it was caused by "@types/react-redux", remove this types definition file and everything works the way you would expect it to without any other side effects/type errors caused by not having that type-def-file.
我遇到了同样的问题,发现它是由“@types/react-redux”引起的,删除这个类型定义文件,一切都按照你期望的方式工作,没有任何其他副作用/类型错误导致没有该类型 - def 文件。