首页 > 解决方案 > 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 中使用异步行为,但我似乎无法找到有关如何实现这一点的信息。

如果我能更好地解释这种情况,请告诉我!

标签: javascriptnode.jspromisegatsby

解决方案


重写后,这似乎解决了我遇到的问题。老实说,我仍在努力完全了解 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,
            },
        })
    })
};

推荐阅读