簡體   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