直接导入 startTransition 与从 useTransition hook 中获取

问题描述 投票:0回答:1

我正在学习 Firebase、构建 React 应用程序(React 18.2.0)的课程。 为了防止应用程序在设置状态时崩溃/冻结,我们将该状态设置包装到一个

startTransition()
函数中。

我们可以直接从 React 导入这个函数或者从 hook 中获取它:

const [isPending, startTransition] = useTransition();

到目前为止,我认为这两种方法之间的唯一区别(也根据文档)是: A.我们只能在顶层功能组件和自定义钩子上使用钩子 B. 使用钩子为我们提供了方便的

isPending
布尔值,指示过渡是否仍在进行,因此可以使用它在 UI 中反映它。

但是,如果我直接导入

startTransition
,它可以工作,但是用完全相同的代码从钩子中取出它,而不使用
isPending
,它不会,它会冻结并崩溃。

我尽力寻找答案。我想了解我做错了什么。可能是因为我用了TypeScript,但是课程没有,需要打字?

这是 App.tsx 的代码,我在其中注释掉了一种方法或另一种方法。抱歉,有好几行,

startTransition()
在第99行,我们在这里声明:

const handleEditRecipeClick = (recipeId: string) => {
startTransition(() => {
  const selectedRecipe = recipes.find((recipe) => recipeId === recipe.id);
  if (selectedRecipe) {
    setCurrentRecipe(selectedRecipe);
    console.log(selectedRecipe);
  }
});
window.scrollTo(0, document.body.scrollHeight);

};

任何线索将不胜感激:)

import {
  useCallback,
  useEffect,
  useState,
  useTransition,
  // startTransition,
} from "react";
import firebase from "firebase/compat";
import "./App.css";
import FirebaseAuthService from "./FirebaseAuthService";
import FirebaseFirestoreService, {
  queryType,
  recipeType,
  recipeWithIdType,
} from "./FirebaseFirestoreService";
import LoginForm from "./components/LoginForm";
import AddEditRecipeForm from "./components/AddEditRecipeForm";

function App() {
  const [isPending, startTransition] = useTransition();
  
  const [user, setUser] = useState<firebase.User | null>(null);
  const [currentRecipe, setCurrentRecipe] = useState<recipeWithIdType | null>(
    null
  );
  const [recipes, setRecipes] = useState<recipeWithIdType[]>([]);


  const fetchRecipes = useCallback(async () => {
    const queries: queryType[] = [];

    if (!user) {
      queries.push({
        field: "isPublished",
        condition: "==",
        value: true,
      });
    }
    try {
      const response = await FirebaseFirestoreService.readDocuments(
        "recipes",
        queries
      );
      const newRecipes = response.docs.map((recipeDoc) => {
        const id = recipeDoc.id;
        const data = recipeDoc.data() as recipeType;
        return { ...data, id };
      });
      setRecipes(newRecipes);
    } catch (error) {
      if (error instanceof Error) {
        alert(error.message);
      }
    }
  }, [user]);

  useEffect(() => {
    fetchRecipes();
  }, [fetchRecipes, user]);

  FirebaseAuthService.subcribeToAuthCahnges(setUser);

  const handleAddRecipe = async (newRecipe: recipeType) => {
    try {
      const response = await FirebaseFirestoreService.createDocument(
        "recipes",
        newRecipe
      );
      fetchRecipes();
      alert(`New recipe created with ID: ${response.id}`);
    } catch (error) {
      if (error instanceof Error) {
        alert(error.message);
      }
    }
  };

  const handleUpdateRecipe = async (
    updatedRecipe: recipeType,
    recipeId: string
  ) => {
    try {
      await FirebaseFirestoreService.updateDocument(
        "recipes",
        recipeId,
        updatedRecipe
      );
      fetchRecipes();
      alert(`New recipe created with ID: ${recipeId}`);
      setCurrentRecipe(null);
    } catch (error) {
      if (error instanceof Error) {
        alert(error.message);
      }
    }
  };

  const handleEditRecipeClick = (recipeId: string) => {
    startTransition(() => {
      const selectedRecipe = recipes.find((recipe) => recipeId === recipe.id);
      if (selectedRecipe) {
        setCurrentRecipe(selectedRecipe);
        console.log(selectedRecipe);
      }
    });
    window.scrollTo(0, document.body.scrollHeight);
  };

  const handleEditRecipeCancel = () => {
    setCurrentRecipe(null);
  };

  //can be more detailed here about types and constatnts, but this is simple
  //more info in button types in crwn-clothing project
  const CATEGORIES = {
    breadsSandwichesAndPizza: "Breads, Sandwiches and Pizza",
    eggsAndBreakfast: "Eggs & Breakfast",
    dessertsAndBakedGoods: "Desserts & Baked Goods",
    fishAndSeafood: "Fish & Seafood",
    vegetables: "Vegetables",
  };
  const lookupCategoryLabel = (categoryKey: string) =>
    CATEGORIES[categoryKey as keyof typeof CATEGORIES];

  //formating the date
  const formatDate = (timestamp: number) => {
    const date = new Date(timestamp);
    const day = date.getUTCDate();
    const month = date.toLocaleString("default", { month: "long" });
    const year = date.getFullYear();

    return `${day} of ${month}, ${year}`;
  };

  return (
    <div className="App">
      <div className="title-row">
        <h1 className="title">Firebase Recipes</h1>
        <LoginForm existingUser={user} />
      </div>
      <div className="main">
        <div className="center">
          <div className="recipe-list-box">
            {recipes && recipes.length > 0 && (
              <div className="recipe-list">
                {recipes.map((recipe) => (
                  <div className="recipe-card" key={recipe.id}>
                    {!recipe.isPublished && (
                      <div className="unpublished">UNPUBLISHED</div>
                    )}
                    <div className="recipe-name">{recipe.name}</div>
                    <div className="recipe-field">
                      Category: {lookupCategoryLabel(recipe.category)}
                    </div>
                    <div className="recipe-field">
                      Published: {formatDate(recipe.publishDate)}
                    </div>
                    {user && (
                      <button
                        type="button"
                        className="primary-button edit-button"
                        onClick={() => handleEditRecipeClick(recipe.id)}
                      >
                        EDIT
                      </button>
                    )}
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>
        {/* {isPending && <p>Loading...</p>} */}
        {user && (
          <AddEditRecipeForm
            //  existingRecipe={currentRecipe}
            onAddRecipe={handleAddRecipe}
            // onUpdateRecipe={handleUpdateRecipe}
            // onCancel={handleEditRecipeCancel}
          />
        )}
      </div>
    </div>
  );
}

export default App;

如果我注释掉 useTransition 钩子,并直接从 React 导入函数,那么一切都有效。据我了解,它应该是相同的功能!我尝试用钩子实现方法,玩额外的功能并更好地理解它,但即使我只使用

startTransition()
而不更改任何代码,它也不起作用

reactjs typescript firebase react-hooks
1个回答
0
投票

useTransition
导致 2 次重新渲染。

https://github.com/facebook/react/issues/24269

我面临着同样的问题,就我而言,问题是我没有考虑将

isPending
切换到
true
的第一次“紧急”重新渲染。

这里有一篇好文章详细解释了这一点

© www.soinside.com 2019 - 2024. All rights reserved.