reactjs - Const 未定义 -> 然而标准解决方案不起作用

标签 reactjs firebase

我想显示一个 mapped list其中“用户名”是来自 Firebase 实时数据库的条目值,对应于每个条目的作者。

以下代码,在 get(UsernameRef).then((snapshot) =>{}) 范围内,按预期返回 undefined reference 错误,'UserName' 被分配了一个值但从未使用过 并且 'UserName' 未定义

  const [RecipeLibrary, setRecipeLibrary] = React.useState([]);
  React.useEffect(() => {
    const RecipeLibraryRef = ref(db, "Recipes/");
    onValue(RecipeLibraryRef, (snapshot) => {
      const RecipeLibrary = [];
      snapshot.forEach((child) => {
        const AuthorUserId = child.key;

        child.forEach((grandChild) => {
          const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");
          get(UserNameRef).then((snapshot) => {
            const UserName = snapshot.val();
          });
          RecipeLibrary.push({
            name: grandChild.key,
            author: UserName,
            ...grandChild.val(),
          });
        });
      });
      setRecipeLibrary(RecipeLibrary);
      console.log({ RecipeLibrary });
    });
  }, []);

我试过:

  • 使用 React 状态传递变量 -> 不能在 React useEffect 中使用
  • 导出和导入返回所需用户名的单独函数 -> 返回只能在内部范围内使用
  • 将列表 .push 移动到 Firebase get 范围内 -> React.useState 无法再访问列表

我希望这里有一个简单的解决方案,因为我是新手。 您的时间和建议意义重大,谢谢!

更新:

通过将数组 .push 移动到 .then 范围内,我获得了 RecipeLibrary 数组以包含所需的“UserName”条目,并将其命名为 authorHere is a log该数组在设置时(第 59 行)和重新渲染时(第 104 行)。

    child.forEach((grandChild) => {
      const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");
      get(UserNameRef).then((snapshot) => {
        const UserName = snapshot.val();
        RecipeLibrary.push({
          name: grandChild.key,
          author: UserName,
          authorId: AuthorUserId,
          ...grandChild.val(),
        });
      });
    });
  });
  setRecipeLibrary(RecipeLibrary);
  console.log(RecipeLibrary);

但是,现在映射列表是not rendering at all在屏幕上。

只是添加了一些上下文,对原始代码进行了极小的改动,我一直坚持这一点,以至于我正在考虑在这一点上完全重写以唤起我的内存。哦,这里是呈现映射列表的位,以防万一:

<Box width="75%" maxHeight="82vh" overflow="auto">
            {RecipeLibrary.map((RecipeLibrary) => (
              <Paper
                key={RecipeLibrary.name}
                elevation={3}
                sx={{

等...

最佳答案

这是一个棘手的问题 - 最简单的选择可能是将 push() setRecipeLibrary() 移动到 然后() 回调,因此它们都在同一范围内,但这会产生一些可怕的副作用(例如,为检索到的每个配方触发重新渲染)。

目标(你已经尽力实现)应该是先等待所有食谱加载完毕,然后使用setRecipeLibrary() 将完整列表设置为状态.假设 get() 返回一个 Promise,一种方法是在异步函数中使用 await:

const [RecipeLibrary, setRecipeLibrary] = React.useState([]);
React.useEffect(() => {
  const RecipeLibraryRef = ref(db, "Recipes/");
  onValue(RecipeLibraryRef, (snapshot) => {

    // An async function can't directly be passed to useEffect(), and
    // probably can't be accepted by onValue() without modification,
    // so we have to define/call it internally.
    const loadRecipes = async () => {
      const RecipeLibrary = [];

      // We can't use an async function directly in forEach, so
      // we instead map() the results into a series of Promises
      // and await them all.
      await Promise.all(snapshot.docs.map(async (child) => {
        const AuthorUserId = child.key;

        // Moved out of the grandChild loop, because it never changes for a child
        const UserNameRef = ref(db, "Account/" + AuthorUserId + "/username");

        // Here's another key part, we await the Promise instead of using .then()
        const userNameSnapshot = await get(UserNameRef);
        const UserName = userNameSnapshot.val();

        child.forEach((grandChild) => {    
          RecipeLibrary.push({
            name: grandChild.key,
            author: UserName,
            ...grandChild.val(),
          });
        });
      }));
      
      setRecipeLibrary(RecipeLibrary);
      console.log({ RecipeLibrary });
    };

    loadRecipes();
  });
}, []);

请记住,Promise.all() 在这里不是严格必需的。如果它的使用使它的可读性降低,您可以改为在普通的 for 循环(而不是 forEach)中执行 grandChild 处理,允许您使用 await 而无需映射结果,因为它不会在回调函数中。

如果 snapshot.docs 不可用,但您仍然可以使用 snapshot.forEach(),那么您可以将 Firebase 对象转换为类似于 Convert A Firebase Database Snapshot/Collection To An Array In Javascript 的数组:

// [...]

      // Change this line to convert snapshot
      // await Promise.all(snapshot.docs.map(async (child) => {
      await Promise.all(snapshotToSnapshotArray(snapshot).map(async (child) => {

// [...]

// Define this somewhere visible
function snapshotToSnapshotArray(snapshot) {
    var returnArr = [];

    snapshot.forEach(function(childSnapshot) {
        returnArr.push(childSnapshot);
    });

    return returnArr;
}

请注意,如果 get() 以某种方式返回 Promise...我担心解决方案会不那么简单。

关于reactjs - Const 未定义 -> 然而标准解决方案不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71134872/

相关文章:

javascript - 我的无序列表是从 json 文件呈现的,但我希望每个列表元素周围都有一个边框

javascript - 使用重复的react-redux从数组中删除特定元素

ios - 使用 Firebase 数据填充 Collection View

javascript - 尝试获取 <option> 的计数,因为它在 React 应用程序中被过滤掉了

javascript - ReactJS在Fetch POST时,然后使用then函数来setState

javascript - Firebase.存储: uploading file from React form

javascript - 创建 Firestore 对象数组时出现问题

swift - Swift 上的 Firebase,如果查询为空,如何得到通知?

firebase - 即使坐标在抖动中也不同,用户之间的距离始终为0?

reactjs - 无法访问 react 路由器的历史记录