React Typescript 自定义挂钩:类型“{}”上不存在属性“prop_name”

jen*_*nja 2 typescript reactjs react-hooks

我正在尝试学习 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 '{}'.
Run Code Online (Sandbox Code Playgroud)

联系表格.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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

自定义钩子.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;

Run Code Online (Sandbox Code Playgroud)

小智 7

问题是inputsCustomHook.ts中的初始状态是{}. 然后,你想渲染inputs.subjectinputs.emailinputs.nameinputs.comments。这些属性在空对象上不存在{},这是错误消息告诉您的。

让我们从一些基础知识开始。你有IContact,但你不知道如何处理它。您应该使用它在您期望该签名的任何位置键入数据。首先,message回调。

const message = (inputs: IContact) => {
    alert(`Message Sent!
    Subject: ${inputs.subject}
    Sender: ${inputs.email}
    Name: ${inputs.name}
    Comments: ${inputs.comments}`);
};
Run Code Online (Sandbox Code Playgroud)

又如何useContactForm挂钩?嗯,你可以,但我不认为我会推荐它。当我看着那个钩子时,我没有看到它里面有任何引用IContact. 在这种情况下,钩子更通用

如果只有某种方式可以添加更通用的打字...

嗯,有。我们想要做的是能够传入一个类型以用作其他对象的类型。

const useContactForm = <T>(callback: (state: T) => void) => {
    //...code
}
Run Code Online (Sandbox Code Playgroud)

在这里,我<T>在箭头函数的前面添加了参数。我还输入了回调(value: T) => void以指示回调应该接受一种类型T作为参数并且不返回任何内容。

现在,我们需要键入useState函数。

const [inputs, setInputs] = useState<T>({});
Run Code Online (Sandbox Code Playgroud)

哦哦。{}不匹配T。我们需要一个类型为 的初始状态T。由于T正在传入,我们的初始状态也需要是。

const useContactForm = <T>(callback: (state: T) => void, initialState: T) => {
    const [inputs, setInputs] = useState<T>(initialState);
    // ...code
}
Run Code Online (Sandbox Code Playgroud)

并传进去。

    const {inputs, handleInputChange, handleSubmit} = useContactForm(message, {
        subject: '',
        email: '',
        name: '',
        comments: '',
    });
Run Code Online (Sandbox Code Playgroud)

好的。这基本上就是需要处理的方式。但是,TypeScript 和 Hooks 的代码和使用还存在其他几个问题。

  1. 你需要更多的打字。事件处理程序中的事件参数应该有类型。例如ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
  2. 您正在传递一个函数setInputs而不是新状态。
  3. 您不带参数调用callbackin handleSubmit,但message回调显然正在寻找IContact.
  4. 您正在尝试在类组件中使用钩子而不是函数式组件。您需要将类组件更改为函数,并将对自定义钩子的调用放在函数内部。

这是一些工作代码。

联系表格.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>
    );
};
Run Code Online (Sandbox Code Playgroud)

自定义钩子.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;
Run Code Online (Sandbox Code Playgroud)