elTel
elTel

Reputation: 107

How to fix failing NextJS export - working fine locally but upon export it's failing, dismally :(

I'm working on a video/music streaming application in Next/React.JS, fetching data from a Wordpress API/Backend/headless CMS. It's working great on localhost (though it's real barebones functionality at the moment) - however, when I attempt to export it to create a static front end the export is failing repeatedly with the (ridiculously common and usually straightforward) 'cannot read property of undefined' error.

I've spent the last 12 hours debugging rigorously and scanning here/GH etc, using all combinations of then()/catch() await etc under the sun, but I just can't get it to work and can't for the life of me figure out why. I'm aware 'title' is undefined because the data hasn't been fetched at the time of exporting but how to get this past'next export'? Here's my getInitialProps from single.js - where the problem seems to be, getting no other relevant errors in the console - I'll post the terminal message upon attempting to export below. This is where I've come back to after dozens of versions - it's been a long day, so there may be a silly mistake or two, but this is functioning locally, without any errors.

static async getInitialProps(context) {
    const slug = context.query.slug;
    let post = {};
    // Make request for posts.
    try {
      const response = await axios.get(
        `http://timeline-music-30.local/wp-json/wp/v2/posts?slug=${slug}`
      );

      post = response.data[0];
    } catch (err) {
      console.error(err);
    }

    return { post };

    // Return our only item in array from response to posts object in props.

    console.log("post:", post);
  }

I expect the application to export to a static site successfully, but it fails with the following terminal message:

copying "static build" directory
  launching 3 threads with concurrency of 10 per thread
[==--] 2/4 50% 118/s 0.0s TypeError: Cannot read property 'title' of undefined
    at _default.render (C:\Users\Terry\Documents\github\projects\timeline-music-3.0\nextjs\.next\server\static\YyI9s0TjENSVhc1SFZUcV\pages\single.js:158:35)

Any ideas/help would be greatly appreciated.

Thanks

Terry

Upvotes: 4

Views: 8929

Answers (1)

Oleg Rysakov
Oleg Rysakov

Reputation: 21

First, you can't console.log after return.

Second. Use isomorphic-fetch and this construction, i think that in your case help this:

static async getInitialProps(context) {
    const slug = context.query.slug;
    // Make request for posts.
    const resPost = await fetch('http://timeline-music-30.local/wp-json/wp/v2/posts?slug=${slug}');
    const dataPost = await resPost.json();

    console.log("post:", dataPost.data[0]);
    return { post: dataPost.data[0] };
  }

In component use {this.props.post}. If this not helped, look at my case, it's working in local and production:


In my similar case, I solved about the same problem:

I solved this problem on the site http://computers.remolet.ru/. The task was to produce different content depending on the domain, so the pages request content from the API via fetch. Here is how I solved this problem:

Add to module top:

import getConfig from 'next/config';
const nextConfig = getConfig();
// npm i isomorphic-fetch need
import 'isomorphic-fetch';

Fetching on page:

    static async getInitialProps ({ ctx }) {
        var host = '';

        if (nextConfig && nextConfig.publicRuntimeConfig && nextConfig.publicRuntimeConfig.HOST) {
            // server side
            host = nextConfig.publicRuntimeConfig.HOST;
        } else if (ctx && ctx.req && ctx.req.headers) {
            // front side
            host = 'http://' + ctx.req.headers.host;
        } else {
            // front side
            host = 'http://' + window.location.host;
        }

        const resPricelist = await fetch(host + '/api/pricelist');
        const dataPricelist = await resPricelist.json();

        const resNewPricelist = await fetch(host + '/api/newpricelist');
        const dataNewPricelist = await resNewPricelist.json();

        const resContent = await fetch(host + '/api/content');
        const dataContent = await resContent.json();

        return {
            pricelistData: dataPricelist,
            newPricelistData: dataNewPricelist,
            contentData: dataContent
        };
    }

Using in component:

<Header as="h1" align="center">
    {this.props.contentData.h1}
</Header>

In next.config.js:

module.exports = withCSS(withSass({
    cssModules: true,
    serverRuntimeConfig: {
        PORT: process.env.PORT, // eslint-disable-line no-process-env
        HOST: process.env.HOST, // eslint-disable-line no-process-env
        CONTENT: process.env.CONTENT // eslint-disable-line no-process-env
    },
    publicRuntimeConfig: {
        PORT: process.env.PORT, // eslint-disable-line no-process-env
        HOST: process.env.HOST, // eslint-disable-line no-process-env
        CONTENT: process.env.CONTENT // eslint-disable-line no-process-env
    }
}));

Starting Node with environment:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "HOST='http://localhost:8000' PORT='8000' CONTENT='./db/computers.json' PRICELIST='./db/pricelist/computers.json' PRICELIST2='./db/pricelist/newComputers.json' node server.js",
        "build": "next build",
        "start": "next start"
    },

This working on localhost and server.

Just use isomorphic-fetch and if you fetch from absolute url all you need is construction:

const resPricelist = await fetch(host + '/api/pricelist');
const dataPricelist = await resPricelist.json();
return {
    data: dataPricelist
}

This is the result of approximately 20 hours of trying and reading the Next.js forums. Hope i help you :)

P.S. Don't forget what you can use getInitialProps ({ ctx }) ONLY on page component, not in child components!

Upvotes: 2

Related Questions