繁体   English   中英

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

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

我要将这个 React 项目从 no TypeScript 转换为 TypeScript,我不确定我在这里做错了什么。

这是我正在处理的组件的代码:

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;

我在这个组件中使用 useForm 第三方 React Hook,问题似乎是从钩子本身的函数调用。 现在我得到的错误是当我在第 91 行调用“getValues()”时。它给了我以下错误:

属性类型 'caller' 在映射类型 '{ [K in keyof Function]-?: PathImpl<K & string, Function[K]>; 中循环引用自身; }'。

我是 TypeScript 的新手,我完全不确定我是否理解这意味着什么。 我也进行了一些谷歌搜索,但我无法真正找到解决问题的方法,至少是我理解的方法。

任何帮助将不胜感激!

如果你没有设法解决这个问题,试试这个。 您的界面 searchData 具有 Function 类型。

让我简化这个问题并让您这样想——您正在尝试将 useForm 与内部具有不可序列化类型的类型一起使用,即 Function。表单通常只接受可序列化数据类型作为输入。 直观地想,function 不可能是表单的输入类型吧? 即使在您的代码中,您也仅将“setValue”与“query”一起使用。

因此,对于 useForm,您的类型参数应该省略搜索 function。类似 useForm<Omit<searchData, "search">> 的东西,或者如果您想在上下文使用者中重用它,则为 Omit 设置一个单独的类型并将其传递给 useForm。

那应该解决你的问题。 但正如我所说,我过度简化了这个问题,以便您可以理解如何修复您的代码。 现在,如果您对更多信息感兴趣,问题的症结在于反应挂钩表单的监视 function 接受一个类型参数并递归地查找所提供类型中的键,因此它可以 go 深入嵌套表单值(其中一件事我喜欢 React Hook 形式)。 “功能”作为其中一种类型的问题是 Function 类型在设计上是递归的。 如果您查看 8822996504788 中的 Function 类型,它有一个属性 'caller',其类型为 Function。这实际上意味着,如果 typescript 要继续检查 react hook 形式提供的递归泛型,那么它将陷入无限循环等等它有作用吗? 你做对了,抛出错误以避免混乱!

有人可能会争辩说问题出在 TypeScript 上,但我认为从设计上讲 TS 可以接受该决定。 React hook Form 应该考虑修改 PathImpl 泛型以仅接受没有递归类型的类型参数,例如 Object 和 Function。

我有一个类似的场景,我的一种类型将 javascript Object 类型作为其中一种属性类型。 注意到有点棘手!

PS:如果您将“搜索”属性更改为非递归 function 类型,它也会解决您的问题,即搜索:(数据:任何) => void。 但这将是一个人为和懒惰的解决方案,因为正如我所说,从语义上讲,function 不能是表单的值。

暂无
暂无

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

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