Reputation: 317
My [slug].js file, below, has two nextjs helper functions. The getStaticPaths and getStaticProps are exported. In my case, it creates the path posts/[slug]
. There is one post file added called hello.json
. Now when I navigate to localhost:3000/posts/hello
it errors saying:
TypeError: Cannot read property 'fileRelativePath' of undefined
. For line 10.
This makes sense after seeing that jsonFile
is undefined. As a matter of fact, the whole getStaticProps
is never called, the log in there is never logged. Why is this happening?
Thanks in advance.
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Create the tina form
const [post, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{post.title}
</h1>
);
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticProps({ ...ctx }) {
console.log(1212, ctx);
const { slug } = ctx.params;
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
console.log(121212, content);
return {
props: {
jsonFile: {
fileRelativePath: `/posts/${slug}.json`,
data: content.default,
},
},
};
}
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
const paths = posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
}));
return {
paths,
fallback: true,
};
};
Upvotes: 3
Views: 3564
Reputation: 317
After some more digging around I found the issue, posting here to hopefully help future readers with the same issue.
The culprit was this:
const dynamicPath = `../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(dynamicPath);
Using a variable in a dynamic import does not work, only strings or template literals. I used a variable because of a eslint parsing error that can only be resolved by downgrading to an earlier version of eslint. This causes eslint to not work for me in this file, but okay, at least the function is called.
This combined with the observation that the component code is called before the getStaticProps
is called made the jsonFile variable undefined and the whole component erroring before it'll ever reach the getStaticProps
. You can see that the log starting with 121212
is coming in earlier than 1212
. Terminal logs:
121212 {
fileRelativePath: 'posts/hello.json',
data: { title: 'Not the actual data' }
}
1212 hello
This is counter intuitive to me as I figured it would first get the props and pass them to the component immediately, but sadly, defining default props is needed to work around this.
New code:
import React from 'react';
import glob from 'glob';
import { usePlugin } from 'tinacms';
import { useJsonForm } from 'next-tinacms-json';
const Page = ({ jsonFile }) => {
console.log(121212, jsonFile);
// Get content and form for Tina
const [content, form] = useJsonForm(jsonFile);
// Register it with the CMS
usePlugin(form);
return (
<h1>
{content.title}
</h1>
);
};
Page.defaultProps = {
jsonFile: {
fileRelativePath: 'posts/hello.json',
data: {
title: 'Not the actual data',
},
},
};
export default Page;
/**
* By exporting the async function called getStaticProps from a page, Next.js
* pre-renders this page at build time using the props returned by
* getStaticProps.
*/
export async function getStaticProps({ params: { slug } }) {
console.log(1212, slug);
// This line caused the issue
// const dynamicPath = (`../../posts/${slug}.json`; // for eslint parsing error: "Cannot read property 'range' of null Occurred while linting"
const content = await import(`../../posts/${slug}.json`);
return {
props: {
jsonFile: {
fileRelativePath: `posts/${slug}.json`,
data: content.default,
},
},
};
}
/**
* The getStaticPaths function defines a list of paths that have
* to be rendered to HTML at build time.
*/
export async function getStaticPaths() {
//get all .json files in the posts dir
const posts = glob.sync('posts/**/*.json');
return {
paths: posts.map(file => ({
params: {
slug: `${file.replace('.json', '')}`,
},
})),
fallback: true,
};
}
Upvotes: 2