我正在设置一个快速应用程序来处理我的React应用程序的SSR。我需要在我的其中一个路由上访问Firebase内容,该路由在我的route.js文件中定义为布尔值。
export default [
{
component: Home,
path: '/',
exact: true,
fetchInitialData: true
},
{
component: About,
path: '/about',
fetchInitialData: false
}
];
这是通过我的express应用程序作为普通服务器端应用程序导入的,以确定路由是否将该布尔值设置为 true ,然后我可以将函数的结果作为prop传递给我应用的组件。
app.get('*', async (req, res) => {
//Get the matching route object for the URL
const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}
let interaction = {}
const context = {};
const modules = [];
//If the route object has a fetchInitialData boolean as truthy, set interaction to the result of getDownloadURL
if(activeRoute.fetchInitialData){
interaction = await getDownloadURL(req.query.id)
}
//THIS IS UNDEFINED, despite using async/await.
console.log(interaction)
//Interaction should be set from here. Process rest of app logic
我遇到了使用Firebase API的障碍,因为我无法在我的react应用程序中包含firebase-admin SDK。因此,我必须在快速环境中运行它,并将数据作为道具传递下来,因此,原因是我对所需的数据使用布尔值,而不是将函数直接传递给应用程序。 我不能使用普通的firebase-sdk,因为我需要使用存储getDownloadURL函数,该函数使用XMLHttpRequest,它与服务器(仅与浏览器)不兼容!
我已经在express应用程序文件中定义了自己的getDownloadURL函数,该函数将查询参数作为文档ID,并将处理该文档中存储的图像的downloadURL。
const getDownloadURL = (path) => {
if(path){
firestore.collection('interactions').doc(path).get()
.then(doc => {
bucket
.file(doc.data().photoPath)
.getSignedUrl({
action: 'read',
expires: '03-09-2999'
})
.then(photoURL => {
const interaction = {
date: doc.data().date,
venue: doc.data().venue,
photoURL: photoURL
}
//console.log(interaction) <<<<< this returns the valid data.
return interaction;
}).catch(e => console.log(e));
}).catch(e => console.log(e));
}else{
return {};
}
}
这将处理firebase文档,并最终以存储API中的photoURL结尾,我将其作为对象返回给调用方。
我通过遵循本指南中的以下结构,尝试使用Promises而非async / await:https://tylermcginnis.com/react-router-server-rendering
const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}
const promise = activeRoute.fetchInitialData
? getDownloadURL(req.query.id)
: Promise.resolve()
promise.then((data) => {
//Rest of app logic, data is still undefined
我没有返回函数中的对象,而是返回了Firebase存储承诺并处理了调用者的承诺。
const getDownloadURL = (path) => {
if(path){
firestore.collection('interactions').doc(path).get()
.then(doc => {
return bucket
.file(doc.data().photoPath)
.getSignedUrl({
action: 'read',
expires: '03-09-2999'
})
}).catch(e => console.log(e));
}else{
return {};
}
}
但是,此函数的结果始终是不确定的,我不知道该如何解决。任何帮助将非常感激。谢谢。
答案 0 :(得分:0)
谢谢@KhauriMcClain,我对您所说的内容进行了一些代码重构,并使它正常工作,我最终处理了调用方的承诺,但我能够将所需的数据获取到完美的应用程序中。需要注意的一件事是我必须执行渲染逻辑,因为交互仅在.then范围内定义,并且我不想复制和粘贴代码。
app.get('*', async (req, res, next) => {
const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}
let interaction = {}
//If the route object has a fetchInitialData boolean as truthy
// set interaction to the result of getDownloadURL
if(activeRoute.fetchInitialData && req.query.id){
interaction = await getDownloadURL(req.query.id)
.then(doc => {
bucket
.file(doc.data().photoPath)
.getSignedUrl({
action: 'read',
expires: '03-09-2999'
})
.then(photoURL => {
interaction = {
date: doc.data().date,
venue: doc.data().venue,
photoURL: photoURL
}
renderApp(req, res, interaction);
}).catch(next)
}).catch(next)
}else{
renderApp(req, res, interaction);
}
})
getDownloadURL就是这样
const getDownloadURL = (path) => {
if(path){
return firestore.collection('interactions').doc(path).get()
}else{
return Promise.resolve({});
}
}
有没有更好的方法来解决这个问题?这似乎是一个笨拙的解决方案,因为我必须将req和res传递给另一个函数来处理渲染逻辑。
答案 1 :(得分:0)
您是否可能会对此进行优化?
将async-await
与then-catch
混合对于可读性不是很好。如果可以提供帮助,则可能需要进一步重构,以便您的代码仅使用async-await
。
我不能保证下面的代码照原样工作,但这应该可以帮助您更好地了解在这里可以做些什么来重构代码。
1)获取Firestore文档的代码不太冗长,而且您似乎需要getDownloadURL
函数之外的文档,因此您可以考虑将其移至主要函数中:
if(activeRoute.fetchInitialData && req.query.id){
const snap = await firestore.collection('interactions').doc(req.query.id).get()
const data = snap.data()
// ...
2)您可以使getDownloadURL
成为异步函数,而不必担心返回承诺。异步函数隐式返回诺言。此外,getDownloadURL
可以接收photoPath,获取photoURL,然后返回它,而不是使用文档路径。
const getDownloadURL = async (photoPath) => {
if(!photoPath){
return {}
}
const photoURL = await bucket.file(photoPath)
.getSignedUrl({
action: 'read',
expires: '03-09-2999'
})
return photoURL // assuming here that photoURL is an object
}
3)最后,您可以将其余代码替换为async-await。我还在这里简化了一些逻辑。
app.get('*', async (req, res, next) => {
const activeRoute = routes.find((route) => matchPath(url.parse(req.url).pathname, route)) || {}
let interaction = {}
//If the route object has a fetchInitialData boolean as truthy
// set interaction to the result of getDownloadURL
if(activeRoute.fetchInitialData && req.query.id){
try {
const snap = await firestore.collection('interactions').doc(req.query.id).get()
const data = snap.data()
const photoURL = await getDownloadURL(data.photoPath)
interaction = {
date: data.date,
venue: data.venue,
photoURL: photoURL
}
} catch (error) {
return next(error) // return is important here
}
}
renderApp(req, res, interaction);
})
您在这里的另一种选择是将getDownloadURL
重命名为getInteraction
,让它返回完整的交互作用,而不仅仅是URL,但这取决于您。
这似乎是一个笨拙的解决方案,因为我必须将req和res传递给另一个函数来处理渲染逻辑。
我个人认为这没有任何问题。