javascript - Gatsby createPages 生命周期方法在 onCreateNode 方法完成添加所需节点字段之前运行
问题描述
我正在使用 gatsby-source-wordpress 的 Gatsby 网站上工作。在我的 gatbsy-node.js 文件中,我使用 onCreateNote 生命周期方法来确定节点是否是某个 WordPress 自定义帖子类型,然后我使用单独的 API 来获取帖子类型的相关信息,使用 createNodeField 添加它作为字段,有时还使用 createRemoteFileNode 将来自 API 的图像添加到新节点上的字段。
现在这在大多数情况下都很好用,但有时 createPages 生命周期方法会在图像/节点代码仍在发生时运行(我相信)。这意味着图像字段还不存在,页面创建失败。然后在它失败后,我在我设置的日志中看到一条控制台消息,它通知我新字段已成功添加到节点。
在 createPages 生命周期运行之前,如何确保所有这些节点都已完成并且数据已完成?似乎当客户端上传更大的图像时,这更有可能失败......如果我正确理解这一点,这是有道理的。这是我的 gatsby-node.js 文件中的代码:
const path = require(`path`);
const slash = require(`slash`);
const fetch = require('node-fetch');
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, actions, store, cache,createNodeId, }) => {
const { createNode, createNodeField } = actions;
function getData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => response.json())
.then((data) => {
resolve(data);
});
})
}
if( node.internal.type === "wordpress__wp_location"){
const yextID = node.acf.yext_entity_id;
const yextOrthos = node.acf.location_orthodontists;
try {
const getLocation = async () => {
const data = await fetch("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareFacility")
.then(response => response.json());
// Transform the data into json
if( data && data.response && data.response.count === 1 ){
createNodeField({
node,
name: `yextLocation`,
value: data.response.entities[0]
});
} else {
console.log("NO LOCATIONS FOUND");
}
};
function getOrthos(){
let orthodontists = [];
yextOrthos.forEach( (ortho, i) => {
orthodontists.push(getData("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + ortho.acf.yext_entity_ortho_id + "%22%7D&entityTypes=healthcareProfessional"));
});
Promise.all(orthodontists).then( (orthoData) => {
if( orthoData.length ){
let finalOrthos = [];
orthoData.forEach( (finalOrtho, x) => {
finalOrthos.push(finalOrtho.response.entities[0]);
});
createNodeField({
node,
name: `yextOrthos`,
value: finalOrthos
});
} else {
console.log("NO DOCTORS FOUND");
}
});
}
getLocation();
getOrthos();
} catch (error) {
console.log(error);
}
}
if( node.internal.type === "wordpress__wp_orthodontist"){
const yextID = node.acf.yext_entity_ortho_id;
const wpID = node.wordpress_id;
try {
const getTextOrtho = async () => {
const data = await fetch("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareProfessional")
.then(response => response.json());
// Transform the data into json
if( data && data.response && data.response.count === 1 ){
googleProfilePhoto = data.response.entities[0].googleProfilePhoto.url;
createNodeField({
node,
name: `yextOrthodontist`,
value: data.response.entities[0]
});
if( data.response.entities[0].googleProfilePhoto && data.response.entities[0].googleProfilePhoto.url){
createNodeField({
node,
name: `yextProfilePicture`,
value: data.response.entities[0].googleProfilePhoto.url
});
let fileNode = await createRemoteFileNode({
url: data.response.entities[0].googleProfilePhoto.url, // string that points to the URL of the image
parentNodeId: node.id, // id of the parent node of the fileNode you are going to create
createNode, // helper function in gatsby-node to generate the node
createNodeId, // helper function in gatsby-node to generate the node id
cache, // Gatsby's cache
store, // Gatsby's redux store
})
// if the file was created, attach the new node to the parent node
if (fileNode) {
console.log("GOOGLE PROFILE NODE CREATED!")
node.featuredImg___NODE = fileNode.id
} else {
console.log("ERROR! fileNode not Created!");
}
} else {
console.log("NO GOOGLE PROFILE PHOTO FOUND");
}
} else {
console.log("NO ORTHODONTISTS FOUND");
}
}
const getWpLocations = async () => {
const data = await fetch(process.env.GATSBY_WP_BASEURL+ "/wp-json/custom_endpoint/v1/locations_by_orthodontist?orthodontist_id=" + wpID).then(response => response.json());
if( data ){
createNodeField({
node,
name: `wpLocations`,
value: data
});
} else {
console.log("NO ORTHODONTISTS FOUND");
}
}
getTextOrtho();
getWpLocations();
} catch (error) {
console.log(error);
}
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
{
locations: allWordpressWpLocation(filter: {status: {eq: "publish"}}) {
nodes {
id
path
acf {
location_orthodontists {
acf {
yext_entity_ortho_id
}
}
yext_entity_id
}
}
}
pages: allWordpressPage(
filter: {
wordpress_id: {nin: [177, 183, 8, 42, 44, 185, 46]}
status: {eq: "publish"}
}) {
nodes {
id
wordpress_id
path
}
}
orthodontists: allWordpressWpOrthodontist(filter: {status: {eq: "publish"}}) {
nodes {
id
path
}
}
posts: allWordpressPost(filter: {status: {eq: "publish"}}) {
nodes {
slug
id
}
}
}
`);
// Check for any errors
if (result.errors) {
throw new Error(result.errors);
}
const { locations, pages, orthodontists, posts } = result.data;
const locationTemplate = path.resolve(`./src/templates/location.js`);
const pageTemplate = path.resolve(`./src/templates/page.js`);
const orthoTemplate = path.resolve(`./src/templates/orthodontist.js`);
const postTemplate = path.resolve(`./src/templates/post.js`);
const blogTemplate = path.resolve(`./src/templates/blog.js`);
locations.nodes.forEach(node => {
let orthodontists = [];
node.acf.location_orthodontists.forEach(ortho => {
orthodontists.push(ortho.acf.yext_entity_ortho_id);
});
let orthodontistList = orthodontists.join();
createPage({
path: `${node.path}`,
component: slash(locationTemplate),
context: {
id: node.id,
yextId: node.acf.yext_entity_id,
yextOrthoIds: orthodontists
},
});
});
pages.nodes.forEach(node => {
createPage({
path: `${node.path}`,
component: slash(pageTemplate),
context: {
id: node.id,
},
});
});
orthodontists.nodes.forEach(node => {
createPage({
path: `${node.path}`,
component: slash(orthoTemplate),
context: {
id: node.id,
},
});
});
posts.nodes.forEach(node => {
createPage({
path: `${node.slug}`,
component: slash(postTemplate),
context: {
id: node.id,
},
});
});
const postsPerPage = 12;
const numPages = Math.ceil(posts.nodes.length / postsPerPage);
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/blog` : `/blog/page/${i + 1}`,
component: slash(blogTemplate),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
})
})
};
感谢您提供的任何信息!我想这可能是因为我仍在学习在 JS 中使用异步行为,但我似乎无法找到有关如何实现这一点的信息。
如果我能更好地解释这种情况,请告诉我!
解决方案
重写后,这似乎解决了我遇到的问题。老实说,我仍在努力完全了解 JS 中 async/await/promises 功能的来龙去脉,但希望如果有人遇到类似问题,查看此重写可能会有所帮助:
const path = require(`path`);
const slash = require(`slash`);
const fetch = require('node-fetch');
const { createRemoteFileNode } = require(`gatsby-source-filesystem`)
exports.onCreateNode = async ({ node, actions, store, cache,createNodeId, }) => {
const { createNode, createNodeField } = actions;
const getData = async (url) => {
return new Promise((resolve, reject) => {
fetch(url)
.then((response) => response.json())
.then((data) => {
resolve(data);
});
})
}
const getLocation = async (yextID) => {
const data = await getData("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareFacility");
// Transform the data into json
if( data && data.response && data.response.count === 1 ){
createNodeField({
node,
name: `yextLocation`,
value: data.response.entities[0]
});
} else {
console.log("NO LOCATIONS FOUND");
}
};
const getOrthos = async (yextOrthos) => {
let orthodontists = [];
yextOrthos.forEach( (ortho, i) => {
orthodontists.push(getData("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + ortho.acf.yext_entity_ortho_id + "%22%7D&entityTypes=healthcareProfessional"));
});
Promise.all(orthodontists).then( (orthoData) => {
if( orthoData.length ){
let finalOrthos = [];
orthoData.forEach( (finalOrtho, x) => {
finalOrthos.push(finalOrtho.response.entities[0]);
});
createNodeField({
node,
name: `yextOrthos`,
value: finalOrthos
});
} else {
console.log("NO DOCTORS FOUND");
}
});
};
const getTextOrtho = async (yextID) => {
const data = await getData("https://api.yext.com/v2/accounts/me/entities?api_key=" + process.env.YEXT_API_KEY + "&v=20191114&filter=%7B%22%24anywhere%22%3A%20%22" + yextID + "%22%7D&entityTypes=healthcareProfessional");
if( data && data.response && data.response.count === 1 ){
createNodeField({
node,
name: `yextOrthodontist`,
value: data.response.entities[0]
});
if( data.response.entities[0].googleProfilePhoto && data.response.entities[0].googleProfilePhoto.url){
createNodeField({
node,
name: `yextProfilePicture`,
value: data.response.entities[0].googleProfilePhoto.url
});
let fileNode = await createRemoteFileNode({
url: data.response.entities[0].googleProfilePhoto.url, // string that points to the URL of the image
parentNodeId: node.id, // id of the parent node of the fileNode you are going to create
createNode, // helper function in gatsby-node to generate the node
createNodeId, // helper function in gatsby-node to generate the node id
cache, // Gatsby's cache
store, // Gatsby's redux store
});
// if the file was created, attach the new node to the parent node
if (fileNode) {
node.featuredImg___NODE = fileNode.id;
console.log("GOOGLE PROFILE NODE CREATED!")
} else {
console.log("ERROR! fileNode not Created!");
}
} else {
console.log("NO GOOGLE PROFILE PHOTO FOUND");
}
} else {
console.log("NO ORTHODONTISTS FOUND");
}
};
const getWpLocations = async (wpID) => {
const data = await getData(process.env.GATSBY_WP_BASEURL+ "/wp-json/perch_endpoint/v1/locations_by_orthodontist?orthodontist_id=" + wpID);
if( data ){
createNodeField({
node,
name: `wpLocations`,
value: data
});
} else {
console.log("NO ORTHODONTISTS FOUND");
}
}
if( node.internal.type === "wordpress__wp_location"){
const yextID = node.acf.yext_entity_id;
const yextOrthos = node.acf.location_orthodontists;
try {
await getLocation(yextID);
await getOrthos(yextOrthos);
} catch (error) {
console.log(error);
}
}
if( node.internal.type === "wordpress__wp_orthodontist"){
const yextID = node.acf.yext_entity_ortho_id;
const wpID = node.wordpress_id;
try {
await getTextOrtho(yextID);
await getWpLocations(wpID);
} catch (error) {
console.log(error);
}
}
}
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions;
const result = await graphql(`
{
locations: allWordpressWpLocation(filter: {status: {eq: "publish"}}) {
nodes {
id
path
acf {
location_orthodontists {
acf {
yext_entity_ortho_id
}
}
yext_entity_id
}
}
}
pages: allWordpressPage(
filter: {
wordpress_id: {nin: [177, 183, 8, 42, 44, 185, 46]}
status: {eq: "publish"}
}) {
nodes {
id
wordpress_id
path
}
}
orthodontists: allWordpressWpOrthodontist(filter: {status: {eq: "publish"}}) {
nodes {
id
path
}
}
posts: allWordpressPost(filter: {status: {eq: "publish"}}) {
nodes {
slug
id
}
}
}
`);
// Check for any errors
if (result.errors) {
throw new Error(result.errors);
}
const { locations, pages, orthodontists, posts } = result.data;
const locationTemplate = path.resolve(`./src/templates/location.js`);
const pageTemplate = path.resolve(`./src/templates/page.js`);
const orthoTemplate = path.resolve(`./src/templates/orthodontist.js`);
const postTemplate = path.resolve(`./src/templates/post.js`);
const blogTemplate = path.resolve(`./src/templates/blog.js`);
locations.nodes.forEach(node => {
let orthodontists = [];
node.acf.location_orthodontists.forEach(ortho => {
orthodontists.push(ortho.acf.yext_entity_ortho_id);
});
let orthodontistList = orthodontists.join();
createPage({
path: `${node.path}`,
component: slash(locationTemplate),
context: {
id: node.id,
yextId: node.acf.yext_entity_id,
yextOrthoIds: orthodontists
},
});
});
pages.nodes.forEach(node => {
createPage({
path: `${node.path}`,
component: slash(pageTemplate),
context: {
id: node.id,
},
});
});
orthodontists.nodes.forEach(node => {
createPage({
path: `${node.path}`,
component: slash(orthoTemplate),
context: {
id: node.id,
},
});
});
posts.nodes.forEach(node => {
createPage({
path: `${node.slug}`,
component: slash(postTemplate),
context: {
id: node.id,
},
});
});
const postsPerPage = 12;
const numPages = Math.ceil(posts.nodes.length / postsPerPage);
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? `/blog` : `/blog/page/${i + 1}`,
component: slash(blogTemplate),
context: {
limit: postsPerPage,
skip: i * postsPerPage,
numPages,
currentPage: i + 1,
},
})
})
};
推荐阅读
- mysql - Hibernate创建没有关系表的关系
- android - Volley to Retrofit2 - 如何使用 Retrofit2 进行字符串请求?
- javascript - 我成功构建了我的应用程序,但是当我运行它时,它崩溃了,这是我的 logcat 中提示的味精
- c# - ASP.NET C# OData 服务 + 导航属性 + $expand = null。我错过了什么?
- select - Erlang Mnesia select on an ordered_set 是否给出了 Erlang Term 顺序的列表?
- jquery - 如何使用 jQuery 按两列对表格进行排序?
- django - 如何创建具有特定区域的 Heroku 应用程序
- c++ - Int 到 ASCII 字符和 char 到 ASCII 数字 | C++
- python - 是否可以在 Tesseract 中提供时间模式?
- xcode - 我的颤振项目没有在 iOS 模拟器中打开