How to create pages from non-seriazable data(functions)

I have this JavaScript data file(src/test/test.js):

module.exports = {
    "title": "...",
    "Number": "number1",
    "Number2": ({ number1 }) => number1 / 2,
}

I want to pass this file verbatim(functions preserved) to a page, so that the page can use that data to build itself. I already have the page template and everything else sorted out, I just need to find a way to pass this into the page.


The first approach I tried is requireing this file in gatsby-node.js and then passing it as pageContext.

  • gatsby-node.js
const path = require('path');

exports.createPages = ({actions, graphql}) => {
    const { createPage } = actions;

    return graphql(`
        query loadQuery {
            allFile(filter: {sourceInstanceName: {eq: "test"}}) {
                edges {
                    node {
                        relativePath
                        absolutePath
                    }
                }
            }
        }
    `).then(result => {
        if (result.errors) {
            throw result.errors;
        }

        for (const node of result.data.allFile.edges.map(e => e.node)) {
            const data = require(node.absolutePath);
            createPage({
                path: node.relativePath,
                component: path.resolve('./src/templates/test.js'),
                context: data,
            });
        }
    });
};
  • gatsby-config.js
module.exports = {
    plugins: [
        {
            resolve: `gatsby-source-filesystem`,
            options: {
                name: `test`,
                path: `${__dirname}/src/test/`,
            },
        },
    ],
}
  • src/templates/test.js
import React from 'react';

const index = ({ pageContext }) => (
    <p>{pageContext.Number2()}</p>
);

export default index;

However, I get this warning when running the dev server:

warn Error persisting state: ({ number1 }) => number1 / 2 could not be cloned.

If I ignore it and try to use the function anyway, Gatsby crashes with this error:

WebpackError: TypeError: pageContext.Number2 is not a function

After searching for a while, I found this:

The pageContext was always serialized so it never worked to pass a function and hence this isn't a bug. We might have not failed before though.
- Gatsby#23675

which told me this approach wouldn't work.

How could I pass this data into a page? I've considered JSON instead, however, JSON can't contain functions.

I've also tried finding a way to register a JSX object directly, however I couldn't find a way.


Regarding the main topic, as you spotted, can't be done that way because the data is serialized.

How could I pass this data into a page? I've considered JSON instead, however, JSON can't contain functions.

Well, this is partially true. You can always do something like:

{"function":{"arguments":"a,b,c","body":"return a*b+c;"}}

And then:

let func = new Function(function.arguments, function.body);

In this case, you are (de)serializing a JSON function, creating and casting a function based on JSON parameters. This approach may work in your scenario.

Regarding the JSX, I guess you can try something like:

for (const node of result.data.allFile.edges.map(e => e.node)) {
    const data = require(node.absolutePath);
    createPage({
        path: node.relativePath,
        component: path.resolve('./src/templates/test.js'),
        context:{
          someComponent: () => <h1>Hi!</h1>
        },
    }); 
}

And then:

import React from 'react';

const Index = ({ pageContext: { someComponent: SomeComponent} }) => (
    return <div><SomeComponent /></div>
);

export default index;

Note: I don't know if it's a typo from the question but index should be capitalized as Index

In this case, you are aliasing the someComponent as SomeComponent, which is a valid React component.