我正在尝试学习React和React Hooks。我创建了生活在另一个文件CustomHook.ts
中的自定义钩子。我在ContactForm.tsx
中使用它。我遇到的问题是value={inputs.property}
标记中的每个<input />
内部。 Typescript无法解析每个inputs._propertyName
的类型。
我已经定义了一个接口IContact
,该接口定义了我想使用的类型。我目前不使用此界面,因为我不知道将其放置在哪里。
任何帮助将不胜感激!
错误:
ERROR in /jefthimi/src/components/Contact/ContactForm.tsx(35,31)
TS2339: Property 'subject' does not exist on type '{}'.
ERROR in /jefthimi/src/components/Contact/ContactForm.tsx(46,31)
TS2339: Property 'email' does not exist on type '{}'.
ERROR in /jefthimi/src/components/Contact/ContactForm.tsx(57,31)
TS2339: Property 'name' does not exist on type '{}'.
ERROR in /jefthimi/src/components/Contact/ContactForm.tsx(68,31)
TS2339: Property 'comments' does not exist on type '{}'.
ContactForm.tsx
import React from 'react';
import './ContactForm.scss';
import useContactForm from './CustomHook';
interface IContact {
subject: string;
email: string;
name: string;
comments: string;
}
const message = (inputs: any) => {
alert(`Message Sent!
Subject: ${inputs.subject}
Sender: ${inputs.email}
Name: ${inputs.name}
Comments: ${inputs.comments}`);
};
const { inputs, handleInputChange, handleSubmit } = useContactForm(message);
export default class ContactForm extends React.Component {
render() {
return (
<div className="contactForm_container">
<div className="contactForm_inner">
<form onSubmit={handleSubmit}>
<div className="input-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
name="subject"
type="text"
onChange={handleInputChange}
value={inputs.subject}
required
/>
</div>
<div className="input-group">
<label htmlFor="email">Your Email</label>
<input
id="email"
name="email"
type="text"
onChange={handleInputChange}
value={inputs.email}
required
/>
</div>
<div className="input-group">
<label htmlFor="name">Your Name</label>
<input
id="name"
name="name"
type="text"
onChange={handleInputChange}
value={inputs.name}
required
/>
</div>
<div className="input-group">
<label htmlFor="comments">Comments</label>
<textarea
name="comments"
id="comments"
rows={10}
onChange={handleInputChange}
value={inputs.comments}
required
/>
</div>
<div className="controls">
<button type="submit">Send Message</button>
</div>
</form>
</div>
</div>
);
}
}
CustomHook.ts
import React, { useState } from 'react';
/*
This is a Custom React Hook that handles our form submission
*/
const useContactForm = (callback) => {
const [inputs, setInputs] = useState({});
const handleSubmit = (event) => {
if (event) {
event.preventDefault();
}
callback();
};
const handleInputChange = (event) => {
event.persist();
setInputs((inputs) => ({
...inputs,
[event.target.name]: event.target.value
}));
};
return {
handleSubmit,
handleInputChange,
inputs
};
};
export default useContactForm;
答案 0 :(得分:3)
问题在于CustomHook.ts中inputs
的初始状态为{}
。然后,您尝试渲染inputs.subject
,inputs.email
,inputs.name
和inputs.comments
。这些属性在空对象{}
上不存在,这是错误消息告诉您的内容。
让我们从一些基础知识开始。您有IContact
,但是您不知道该怎么做。您应该使用它在需要该签名的任何地方键入数据。首先,使用message
回调。
const message = (inputs: IContact) => {
alert(`Message Sent!
Subject: ${inputs.subject}
Sender: ${inputs.email}
Name: ${inputs.name}
Comments: ${inputs.comments}`);
};
useContactForm
钩怎么样?好吧,可以,但是我不建议您这样做。当我查看该钩子时,看不到其中引用IContact
的任何内容。在这种情况下,钩子会更泛型。
如果只有一种添加类型的方式更泛型 ...
嗯,有。我们想要做的就是能够传递一种类型用作其他对象的类型。
const useContactForm = <T>(callback: (state: T) => void) => {
//...code
}
在这里,我在箭头函数参数的前面添加了<T>
。我也将回调键入为(value: T) => void
,以指示回调应接受T
类型的参数作为参数,并且不返回任何内容。
现在,我们需要输入useState
函数。
const [inputs, setInputs] = useState<T>({});
嗯。 {}
与T
不匹配。我们需要类型为T
的初始状态。由于T
被传入,因此我们的初始状态也必须保持不变。
const useContactForm = <T>(callback: (state: T) => void, initialState: T) => {
const [inputs, setInputs] = useState<T>(initialState);
// ...code
}
并将其传递进来。
const {inputs, handleInputChange, handleSubmit} = useContactForm(message, {
subject: '',
email: '',
name: '',
comments: '',
});
好的。这基本上就是需要处理的方式。 但是,代码以及TypeScript和Hooks的使用还有其他一些问题。
ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
setInputs
中,而不是传递新状态。callback
中调用handleSubmit
,但不带参数,但是message
回调显然正在寻找IContact
。这是一些工作代码。
ContactForm.tsx
import React from 'react';
import './ContactForm.scss';
import useContactForm from './CustomHook';
interface IContact {
subject: string;
email: string;
name: string;
comments: string;
}
const message = (inputs: IContact) => {
alert(`Message Sent!
Subject: ${inputs.subject}
Sender: ${inputs.email}
Name: ${inputs.name}
Comments: ${inputs.comments}`);
};
export default () => {
const {inputs, handleInputChange, handleSubmit} = useContactForm(message, {
subject: '',
email: '',
name: '',
comments: '',
});
return (
<div className="contactForm_container">
<div className="contactForm_inner">
<form onSubmit={handleSubmit}>
<div className="input-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
name="subject"
type="text"
onChange={handleInputChange}
value={inputs.subject}
required
/>
</div>
<div className="input-group">
<label htmlFor="email">Your Email</label>
<input
id="email"
name="email"
type="text"
onChange={handleInputChange}
value={inputs.email}
required
/>
</div>
<div className="input-group">
<label htmlFor="name">Your Name</label>
<input
id="name"
name="name"
type="text"
onChange={handleInputChange}
value={inputs.name}
required
/>
</div>
<div className="input-group">
<label htmlFor="comments">Comments</label>
<textarea
name="comments"
id="comments"
rows={10}
onChange={handleInputChange}
value={inputs.comments}
required
/>
</div>
<div className="controls">
<button type="submit">Send Message</button>
</div>
</form>
</div>
</div>
);
};
CustomHook.ts
import React, {useState, FormEvent, ChangeEvent} from 'react';
/*
This is a Custom React Hook that handles our form submission
*/
const useContactForm = <T>(callback: (state: T) => void, initialState: T) => {
const [inputs, setInputs] = useState<T>(initialState);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
if (event) {
event.preventDefault();
}
callback(inputs);
};
const handleInputChange = (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
event.persist();
setInputs({
...inputs,
[event.target.name]: event.target.value,
});
};
return {
handleSubmit,
handleInputChange,
inputs,
};
};
export default useContactForm;
答案 1 :(得分:1)
建议的解决方案是定义一个包含我们的Message属性的接口,然后创建一个具有初始化字段的默认Message对象。
interface IMessage {
subject: string;
email: string;
name: string;
comments: string;
}
const message: IMessage = {
subject: '',
email: '',
name: '',
comments: ''
};
然后使用此初始化的对象设置状态。
const [inputs, setInputs] = useState(message);