简体   繁体   English

需要帮助,尝试使用 TypeScript 将项目从 React 转换为 React

[英]Need help, trying to convert a project from React to React with TypeScript

I am to convert this React project from no TypeScript to TypeScript and I am not sure what I am doing wrong here.我要将这个 React 项目从 no TypeScript 转换为 TypeScript,我不确定我在这里做错了什么。

Here is the code for the component I am working on:这是我正在处理的组件的代码:

import React from "react";
import { useContext, useState, useEffect } from "react";
import styles from "./Search.module.css";
import { BsSearch } from "react-icons/bs";
import axios from "axios";
import { AuthContext } from "../../context/AuthContext";
import { useForm, SubmitHandler } from "react-hook-form";
import { Alert } from "react-bootstrap";
import SearchResult from "./SearchResult/SearchResult";

const Search: React.FC = () => {

  interface searchData {
    query: string | null;
    search: Function
  }

  interface localStorageItems {
    searchTerm: string | null;
    data: Array<any>;
  }

  const {
    register,
    handleSubmit,
    getValues,
    setValue,
    formState: { errors },
  } = useForm<searchData>({
    mode: "onSubmit",
    reValidateMode: "onChange",
    defaultValues: {},
    resolver: undefined,
    context: undefined,
    criteriaMode: "firstError",
    shouldFocusError: true,
    shouldUnregister: false,
  });

  const { state } = useContext(AuthContext);

  const [results, setResults] = useState<Array<any> | null>([]);

  const [isLoading, setIsLoading] = useState<Boolean>(false);

  useEffect(() => {
    const storedResults: localStorageItems = JSON.parse(
      window.localStorage.getItem("results") as string
    );
    if (storedResults) {
      setValue("query" , storedResults.searchTerm );
      setResults(storedResults.data);
    }
  }, [setValue]);

  const [apiError, setApiError] = useState<String>();

  const search: Function = async (data: any) => {
    try {
      setIsLoading(true);
      const asyncResponse = await axios({
        method: "GET",
        url: `/api/book/search/${data.query.split(" ").join("+")}`,
        headers: {
          Authorization: `Bearer ${state.token}`,
          "Content-Type": "application/json",
        },
      });
      if (asyncResponse.status === 200) {
        setIsLoading(false);
        setResults(asyncResponse.data.books);
        window.localStorage.setItem(
          "results",
          JSON.stringify({
            searchTerm: getValues("query"),
            data: asyncResponse.data.books,
          })
        );
      }
    } catch (e) {
      console.log(e);
      if (e.response) {
        setIsLoading(false);
        setApiError(
          "There was a problem with your search, please try again later or contact customer support."
        );
      }
    }
  };

  const searchQuery: string | null = getValues("query");

  const onSubmit: SubmitHandler<searchData> = (data) => search(data);

  return (
    <>
      {apiError && (
        <Alert className="mt-1" variant="danger">
          {apiError}
        </Alert>
      )}
      {isLoading && (
        <Alert className="mt-1" variant="secondary">
          Loading...
        </Alert>
      )}
      <h1 className="text-center">Search</h1>
      <form
        onSubmit={handleSubmit(onSubmit)}
        id={styles["search-form"]}
        className="d-flex col-md-4 my-2"
      >
        <input
          className="form-control me-2"
          type="search"
          placeholder="Enter your search here"
          aria-label="Search"
          onClick={() => {
            setValue("query", null);
            setResults(null);
            window.localStorage.removeItem("results");
          }}
          {...register("query", {
            required: "Please enter a search query",
          })}
        />
        {errors.query && (
          <Alert className="mt-1" variant="danger">
            {errors.query?.type === "required" && errors.query.message}
          </Alert>
        )}
        <button
          id={styles["search-button"]}
          className="btn btn-outline-success"
        >
          {<BsSearch />}
        </button>
      </form>
      {window.localStorage.getItem("results") && (
        <h2 className="text-center">
          Search results for "{searchQuery}"
        </h2>
      )}
      <div id={styles["searchResults"]}>
        {results &&
          results.map((result, index) => {
            return (
              <SearchResult
                key={`searchResult-${index}`}
                title={result.title}
                author={result.authors}
                desc={result.description}
                picture={result.imageLinks?.thumbnail}
                bookId={result.id}
              />
            );
          })}
      </div>
    </>
  );
};

export default Search;

I am using the useForm third party React Hook in this component and the issue seems to be to calls from functions in the hook itself.我在这个组件中使用 useForm 第三方 React Hook,问题似乎是从钩子本身的函数调用。 Now the error I am getting is when I call "getValues()" on line 91. It gives me the following error:现在我得到的错误是当我在第 91 行调用“getValues()”时。它给了我以下错误:

Type of property 'caller' circularly references itself in mapped type '{ [K in keyof Function]-?: PathImpl<K & string, Function[K]>;属性类型 'caller' 在映射类型 '{ [K in keyof Function]-?: PathImpl<K & string, Function[K]>; 中循环引用自身; }'. }'。

I am new to TypeScript and I am not entirely sure I understand what this means, at all.我是 TypeScript 的新手,我完全不确定我是否理解这意味着什么。 I have also done some googling but I cannot really find a solution to the problem, at least one that I understand.我也进行了一些谷歌搜索,但我无法真正找到解决问题的方法,至少是我理解的方法。

Any help would be greatly appreciated!任何帮助将不胜感激!

If you did not manage to solve this, try this.如果你没有设法解决这个问题,试试这个。 Your interface searchData has a Function type.您的界面 searchData 具有 Function 类型。

Let me simplify the issue and make you think of it this way - you are trying to use useForm with a type that has a non serializable type inside it ie Function. A form typically only takes in serializable data types as input.让我简化这个问题并让您这样想——您正在尝试将 useForm 与内部具有不可序列化类型的类型一起使用,即 Function。表单通常只接受可序列化数据类型作为输入。 Intuitively thinking about it, a function cannot be an input type for form, right?直观地想,function 不可能是表单的输入类型吧? Even in your code, you are using 'setValue' with 'query' only.即使在您的代码中,您也仅将“setValue”与“query”一起使用。

So for useForm your type parameter should omit the search function. something like useForm<Omit<searchData, "search">> or have a separate type for the Omit and pass it to useForm if you want to reuse it eg in a context consumer.因此,对于 useForm,您的类型参数应该省略搜索 function。类似 useForm<Omit<searchData, "search">> 的东西,或者如果您想在上下文使用者中重用它,则为 Omit 设置一个单独的类型并将其传递给 useForm。

That should sort you out.那应该解决你的问题。 But as I said, I oversimplified the issue so that you can understand how to fix your code.但正如我所说,我过度简化了这个问题,以便您可以理解如何修复您的代码。 Now, if you are interested for more info, the crux of the issue is that react hook form's watch function takes a type parameter and recursively looks for the keys in the provided type basically so it can go deep into nested form values (One of things i loved about react hook form).现在,如果您对更多信息感兴趣,问题的症结在于反应挂钩表单的监视 function 接受一个类型参数并递归地查找所提供类型中的键,因此它可以 go 深入嵌套表单值(其中一件事我喜欢 React Hook 形式)。 The problem you have with 'Function' as one of those types is that a Function type is recursive by design. “功能”作为其中一种类型的问题是 Function 类型在设计上是递归的。 If you look at the Function type in javascript, it has a property 'caller' whose type is Function. This essentially means, typescript would get into an endless loop if it was to continue checking the recursive generic provided by react hook form and so what does it do?如果您查看 8822996504788 中的 Function 类型,它有一个属性 'caller',其类型为 Function。这实际上意味着,如果 typescript 要继续检查 react hook 形式提供的递归泛型,那么它将陷入无限循环等等它有作用吗? You got it right, throws an error to avoid that mess!你做对了,抛出错误以避免混乱!

One could argue that the issue lies with TypeScript but I think by design TS is okay with that decision.有人可能会争辩说问题出在 TypeScript 上,但我认为从设计上讲 TS 可以接受该决定。 React hook Form should probably consider modifying the PathImpl generic to only accept type parameters with no recursive types such as Object and Function. React hook Form 应该考虑修改 PathImpl 泛型以仅接受没有递归类型的类型参数,例如 Object 和 Function。

I had a similar scenario where one of my types had the javascript Object type as one of the property types.我有一个类似的场景,我的一种类型将 javascript Object 类型作为其中一种属性类型。 Was a bit tricky to notice!注意到有点棘手!

PS: If you were to change your 'search' property to a non-recursive function type, it would also fix your issue ie search: (data: any) => void. PS:如果您将“搜索”属性更改为非递归 function 类型,它也会解决您的问题,即搜索:(数据:任何) => void。 But this would be a contrived and lazy solution because as I said, semantically, a function cannot be a value of a form.但这将是一个人为和懒惰的解决方案,因为正如我所说,从语义上讲,function 不能是表单的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM