通过 express 从 mongo 获取数据,构建对象并发送到 React

Posted

技术标签:

【中文标题】通过 express 从 mongo 获取数据,构建对象并发送到 React【英文标题】:Fetching data from mongo via express, build object, and send to React 【发布时间】:2021-03-20 03:05:56 【问题描述】:

我目前陷入异步地狱。 在我的 React 中,我有一个页面 /menu,它将通过 expressjs api 从我的 mongo 实例加载数据。

在我的名为 menu 的数据库中,我有代表膳食类型的集合,例如“早餐”、“午餐”等。在这些集合中,每个项目的文档看起来像这个面包集合示例:


  _id: 2398jcs9dn2f9f,
  name: "Ciabatta",
  desc: "Italian bread",
  imageURI: "image01.jpg",
  reviews: []

这是我在页面加载时调用的 api

exports.getAllFoods = (req, res, next) => 
    const db = mongoose.connection

    const allCollections = 

    try 
        db.db.listCollections().toArray((err, collections) => 
            collections.forEach((k) => 
                allCollections[k.name] = []
            )

            Object.keys(allCollections).map(k => 
                let Meal = mongoose.model(k, MealSchema)
            
                meal = Meal.find((err, docs) => 
                    allCollections[k] = docs
                    console.log(allCollections)
                )
            )
            res.send(allCollections)
        )
     catch (error) 
        console.log(error)
        res.send('unable to get all collections')
    

console.log(allCollections) 的最后一个输出是这样的:

 snacks:
   [  review: [],
       tags: [],
       _id: 5fcec3fc4bc5d81917c9c1fe,
       name: 'Simosa',
       description: 'Indian food',
       imageURI: 'image02.jpg',
       __v: 0  ],
  breads:
   [  review: [],
       tags: [],
       _id: 5fcec41a4bc5d81917c9c1ff,
       name: 'Ciabatta',
       description: 'Italian bread',
       imageURI: 'image02.jpg',
       __v: 0  ],

这正是我需要的,但我一直在弄清楚如何发送到 React。我该怎么做才能发送上面的 json? res.send(allCollections) 给了我这个:


    "snacks": [],
    "breads": [],
    "drinks": []

我明白为什么要发送上述内容,但我不知道我需要做些什么来解决它。

这是我在页面加载时的反应

useEffect(() => 
        axios
        .get('http://localhost:8888/api/allFoods')
        .then((res) => 
            setMealTypes(res.data)
        )
        .catch((err) => [
            console.log(err)
        ])
    , [])

最终,我需要在控制台中输出 json,因为我想遍历该数据并使用键作为标题,然后列出值数组中的值,例如

<div>
  <h2>Breads</h2>
  <img src=image01.jpg/>
  <h3>Ciabatta</h3>
  <p>Italian bread</p>
  ...
</div> 
...

感谢任何帮助,以及我应该阅读的任何文档以帮助和提高我对 javascript 的理解

【问题讨论】:

【参考方案1】:

我更喜欢使用 async/awaitPromise.all 来解决这个问题,替换大多数回调。

因为你在遍历数组时调用了数据库,所以你遇到了最烦人的回调情况:你如何发出一堆异步的东西,然后得到结果?您需要其他东西来确保在发送结果之前调用所有回调。

Async/await 意味着我们可以声明一个函数是异步的,并等待异步操作的结果。 async/await 在 JS 中很烦人,因为它抽象出回调,实际上是在下面创建一个 Promise。更复杂的是,async/await 不能解决发出 多个 异步函数的问题,所以我们不得不再次依赖这个花哨的 Promise.all() 函数与 map 结合 - 将所需的输入数组传递给异步函数.

原文:

Object.keys(allCollections).map(k => 
  let Meal = mongoose.model(k, MealSchema)
  meal = Meal.find((err, docs) => 
    allCollections[k] = docs
    console.log(allCollections)
  )
);

建议的异步/等待:

await Promise.all(Object.keys(allCollections).map(async k => 
  let Meal = mongoose.model(k, MealSchema)
  let docs = await Meal.find();
  allCollections[k] = docs;
  console.log(allCollections);
));

另一个优点是错误处理。如果原始示例的回调中发生任何错误,则不会在此 try/catch 块中捕获它们。 async/await 会像您期望的那样处理错误,错误最终会出现在 catch 块中。

...
      // Now that we have awaited all async calls above, this should be executed _after_ the async calls instead of before them.
      res.send(allCollections);
    )
   catch (error) 
    console.log(error)
    res.send('unable to get all collections')
  

从技术上讲,Promise.all() 返回一个结果数组,但我们可以忽略这一点,因为无论如何您都在格式化 Object

还有很多空间可以进一步优化。我可能会把整个函数写成这样:

exports.getAllFoods = async (req, res, next) => 
  const db = mongoose.connection.db;

  try 
    let collections = await db.listCollections().toArray();

    let allCollections = ;
    collections.forEach((k) => 
      allCollections[k.name] = [];
    )

    // For each collection key name, find docs from the database
    // await completion of this block before proceeding to the next block
    await Promise.all(Object.keys(allCollections).map(async k => 
      let Meal = mongoose.model(k, MealSchema)
      let docs = await Meal.find();
      allCollections[k] = docs;
    ));

    // allCollections should be populated if no errors occurred
    console.log(allCollections);
    res.send(allCollections);
   catch (error) 
    console.log(error)
    res.send('unable to get all collections')
  

完全未经测试。

您可能会发现这些链接比我的解释更有帮助:

https://javascript.info/async-await

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

https://medium.com/dailyjs/the-pitfalls-of-async-await-in-array-loops-cf9cf713bfeb

【讨论】:

非常感谢。这解决了我的问题。正如您所说,这允许所有回调在发送我的请求之前完成。非常感谢。我将回顾这些资源以更好地理解 async/await【参考方案2】:

希望对您有所帮助:您需要先使用 stringify 方法,然后再从 express api 发送集合,然后在 React 前端使用 JSON.parse 来恢复对象。 PS:可以在 res.send(allCollections) 上面写一个 console.log(allCollections) 吗?

【讨论】:

【参考方案3】:

需要以 JSON 格式发送到前端。

res.send(allCollections) 替换为res.json(allCollections)

【讨论】:

以上是关于通过 express 从 mongo 获取数据,构建对象并发送到 React的主要内容,如果未能解决你的问题,请参考以下文章

nodejs , mongo find 没有返回数据

如何使用node js,reactjs,express从实际网页中的mongodb中获取数据

nodejs+express+mongoose无法获取数据库数据问题解决

通过 express、mongoose 和 angular 删除 mongo 文档时出现 404

mongo-express 没有连接到 mongodb

WebSockets 不会从 Vue 应用程序发送数据 - 错误状态“发送”未定义,堆栈:Vue Express Mongo WebSockets