【问题标题】:How to create a page in Gatsby from JSON file?如何从 JSON 文件在 Gatsby 中创建页面?
【发布时间】:2019-12-07 09:45:30
【问题描述】:

我正在尝试使用 JSON 文件在 Gatsby 中动态创建一个页面。在该文件中,我定义了应在页面中呈现的组件。

我遵循了 Gatsby 的文档,但是,它没有我想要的。所以我尝试通过读取 JSON 文件并遍历其中的组件并使用 React.createElement() 创建它们来创建页面。最后,我得到了一个 react 组件数组,我将这些组件传递给了模板页面组件的 createPage 方法的 context 对象中的 children prop。

这是解决这个想法的正确方法吗?它在盖茨比中是否可行?

我认为说我尝试了动态导入并且效果很好是很有用的,但我正在尝试找到一种方法,我不必将所有组件转储到一个文件夹中。

我有这个项目的 Github 存储库。 https://github.com/ahmedalbeiruti/Gatsby-dynamic-pages

这里是代码的主要部分:

gatsby-node.js

exports.createPages = ({actions})=>{
    const {createPage} = actions
    const resutl = componentsRenderer(data.page.layout.columns)
    createPage({
        path: data.page.name,
        component: path.resolve('./src/template/page.js'),
        context:{
            children: resutl
        }
    })
}

const componentsRenderer = components => {

    return components.map(component => {
        let children = []
        if (component.children){
            children = componentsRenderer(component.children)
        }
        const element = require(`${__dirname}/${component.path}`)

        return React.createElement(element, Object.assign({},{key: component.key},{...component.props}),children)
    });
}

data/sample-page-no-props.json

{
        "page":{
            "name": "about",
            "layout":{
                "columns": [
                    {
                        "key":"column_1",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            {
                                "name": "FirstComponent",
                                "path": "/src/components/custom/first-component.jsx",
                                "key": "first_component_1"
                            }
                        ]
                    },
                    {
                        "key": "column_2",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            {
                                "key": "second_component_1",
                                "name": "SecondComponent",
                                "path": "/src/components/custom/second-component.jsx",
                                "children":[
                                    {
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    }
                                ]
                            },
                            {
                                "key": "third_component_1",
                                "name": "ThirdComponent",
                                "path": "/src/components/custom/third-component.jsx",
                                "children":[
                                    {
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        }
}

data/sample-page-with-style-prop.json(FirstComponent 有 props 对象)

{
        "page":{
            "name": "about",
            "layout":{
                "columns": [
                    {
                        "key":"column_1",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            {
                                "name": "FirstComponent",
                                "path": "/src/components/custom/first-component.jsx",
                                "key": "first_component_1",
                                "props":{
                                    "style":{
                                        "color":"red"
                                    }
                                }
                            }
                        ]
                    },
                    {
                        "key": "column_2",
                        "name": "Column",
                        "path": "/src/components/layouts/column.jsx",
                        "children":[
                            {
                                "key": "second_component_1",
                                "name": "SecondComponent",
                                "path": "/src/components/custom/second-component.jsx",
                                "children":[
                                    {
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    }
                                ]
                            },
                            {
                                "key": "third_component_1",
                                "name": "ThirdComponent",
                                "path": "/src/components/custom/third-component.jsx",
                                "children":[
                                    {
                                        "key": "leaf_component_1",
                                        "name": "LeafComponent",
                                        "path":"/src/components/custom/leaf-component.jsx"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        }
}

模板/page.js

import React from 'react'
import Layout from '../components/layouts/Layout'

const Page = (props)=>{
    console.log(`page_context: ${props.pageContext.children}`)
    return (
        <>
        <h1>this is the about page</h1>
        <Layout>
            {props.pageContext.children}
        </Layout>
        </>
    )
}

export default Page

components/custom/first-component.jsx

// import React from "react";

const React = require("react");

module.exports = (props)=>{
    return(
        <h3 style={props.style}>
            Hi this is the first component
        </h3>
    )
}

// export default FirstComponent

使用 sample-page-with-style-prop.json 文件时遇到的错误如下:

UNHANDLED REJECTION Cannot assign to read only property 'style' of object '#<Object>'

TypeError: Cannot assign to read only property 'style' of object '#<Object>'

如果我更改为 sample-page-no-props.json 文件,我会收到以下错误:

UNHANDLED REJECTION Cannot assign to read only property 'children' of object '#<Object>'

TypeError: Cannot assign to read only property 'children' of object '#<Object>'

【问题讨论】:

    标签: javascript reactjs gatsby react-context


    【解决方案1】:

    而不是将组件作为上下文传递给页面模板。正确的方法是传递数据并在 Page 中渲染组件

    gatsby-node.js

    exports.createPages = ({actions})=>{
        const {createPage} = actions
        const resutl = componentsRenderer(data.page.layout.columns)
        createPage({
            path: data.page.name,
            component: path.resolve('./src/template/page.js'),
            context:{
                children: resutl
            }
        })
    }
    

    template/page.js

    import React from 'react'
    import Layout from '../components/layouts/Layout'
    
    const componentsRenderer = components => {
    
        return components.map(component => {
            let children = []
            if (component.children){
                children = componentsRenderer(component.children)
            }
            const element = require(`${HOME_DIR}/${component.path}`)
    
            return React.createElement(element, Object.assign({},{key: component.key},{...component.props}),children)
        });
    }
    const Page = (props)=>{
        const data = props.pageContext.children
        return (
            <>
            <h1>this is the about page</h1>
            <Layout>
                {componentsRenderer(data)}
            </Layout>
            </>
        )
    }
    
    export default Page
    

    PS:确保页面组件中HOME_DIR路径正确。

    【讨论】:

    • 非常感谢您的回复,我正在尝试您的建议。不幸的是,它没有用。我收到错误:元素类型无效:需要一个字符串(对于内置组件)或一个类/函数(对于复合组件),但得到:对象。这可能是因为我没有将要求更改为 es6 导入(实际上是使用可加载组件),但这将使它成为我已经完成的动态导入并且它工作正常。我正在寻找替代方案,因为我的团队选择不进行动态导入。
    • 似乎您应该将组件导出为默认值并导入为默认值,如const element = require(`${HOME_DIR}/${component.path}`).default
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 2019-12-08
    • 2021-11-05
    • 2021-09-02
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 2021-04-01
    相关资源
    最近更新 更多