Next. Js Step on the pit entry series

  • Hello Next. Js
  • Add Antd && CSS
  • (3) Directory reconstruction && routing again
  • (4) Next. Js intermediate pit filling
  • (5) Introduce state management Redux
  • (6) Reconstruct the directory again
  • (7) Other relevant knowledge

Get data &&getInitialProps

The React application has its own routing component (most of which is a React-router). We can use the routing component to provide methods for us. Such as the onEnter() method of the react-Router or the beforeEnter() method of the universal router.

A route component called universal router that differs from the React-router is recommended here

There is no routing component in Next. Js, so the way it works is different from the way the routing component works. In particular, iT provides a new lifecycle that is different from React — getIntialProps().

Method of use

  • Used in React.Com ponent
   import React from 'react'

   export default class extends React.Component {
     static async getInitialProps({ req }) {
       const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
       return { userAgent }
     }
   
     render() {
       return (
         <div>
           Hello World {this.props.userAgent}
         </div>
       )
     }
   }

Copy the code
  • Used within the Stateless component
   const Page = ({ stars }) =>
     <div>
       Next stars: {stars}
     </div>
   
   Page.getInitialProps = async ({ req }) => {
     const res = await fetch('https://api.github.com/repos/zeit/next.js');
     const json = await res.json();
     return { stars: json.stargazers_count };
   }
   
   export default Page;

Copy the code

The React lifecycle is separate from the React lifecycle, but we can still use the React lifecycle functions in the component.

Server availability

That’s really the magic of the getInitialProps lifecycle. It can run on the server. What’s the benefit of that? To be honest, I really don’t know, I only know a little bit, I will talk about it, haha. If you know, please let me know in the comments.

As you can see, this life cycle I triggered the action to fetch data, and the action is printed on the console, indicating that it can be run on the server

Reduce the number of times to capture data

  • React Obtains data during the old life cycle

    For example, fetching a list of users is available within the component’s componentDidMount lifecycle

 // /components/user/userList.js
 ...
 componentDidMount() {
    this.props.fetchUserList();
 }
Copy the code

As you can see from the figure above, user data is re-fetched every time you enter the user list page. Some people might say, this is nonsense, react is not like this, the route switched. Yes, that’s normal, which is why this new life cycle of next.js is awesome.

  • Use the getInitialProps lifecycle
// /pages/user/userList.js import UserList from '.. /.. /containers/user/UserList'; import { fetchUserListData } from '.. /.. /redux/actions/user'; UserList.getInitialProps = async (props) => { const { store, isServer } = props.ctx; if (store.getState().user.list.list.length === 0) { store.dispatch(fetchUserListData()); } return { isServer }; }; export default UserList;Copy the code

After entering the system, the data will only be obtained when entering the route for the first time. Since the server side has cached the data, there is no need to obtain the data again, which reduces the number of times to obtain

Static getInitialProps() is a lifecycle that can be run on the server. When the page first loads, the server receives the request, getInitialProps() executes the data returned by getInitialProps(), __NEXT_DATA__. Props, as in ={props:{XXX}}. This lets the server getInitialProps() deliver the data to the client. __NEXT_DATA__ the client will fetch the data from window.__next_data__ to render the page, as shown in the following figure.

There is a problem – trampling pits

In fact, there is a pit, maybe a lot of people have encountered, maybe no one has encountered. The problem description looks something like this. We prefetch data in getInitialProps, and the user list, for example, works fine when it’s first loaded, including various client jumps. But when we refreshed the user list page, it didn’t go through the getInitialProps lifecycle, so the page would have no data to render and would be empty because it thought it should be fetched from window.__next_data__. The next. Js page will give us an isServer:true in the props, but the console doesn’t retrieve the data. See the screenshot below for details:

From the screenshot, it is clear that the page data is obtained by redux-saga. In the getIntialProps() of Pages, the code is as follows:

import { fetchUserListData } from '.. /.. /redux/actions/user'; UserList.getInitialProps = async (props) => { const { store, isServer } = props.ctx; if (store.getState().user.list.list.length === 0) { store.dispatch(fetchUserListData()); } return { isServer }; };Copy the code

FetchUserListData () is the action that fetches the data, and the return value is stored in the state, rendering the list of data. Obviously, the first time it was loaded it was fetching successfully. If the page is refreshed, the getIntialProps lifecycle is not supported.

The above is the key problem, it is normal without refreshing the page, refreshing the page does not go through this life cycle, and we need to pre-obtain a lot of data, so it is quite a pit, in fact, many people encountered this problem, and I also found this problem in the official ReUDx-demo of next. That is to say, their official demo refresh will also have this problem.

The solution

There are two ways to solve this problem:

  • The first is to determine the isServer in the component lifecycle

    __NEXT_DATA__ from the browser window.__NEXT_DATA__ to reduce unnecessary network requests. When the page is refreshed, the data is not requested again and the desired data is not found in window.__next_data__. However, console information allows us to find out what the problem is and how to fix it. IsServer returns false when the system is first booted, and true when the browser refreshes the page. We can check this variable in the component, and if true, we will fetch the data again.

// /components/user/UserList.js ... ComponentDidMount () {if (this. Props. IsServer) {/ / need to grab data this. Props. FetchUserListData (); }}...Copy the code

As you can see from the image above, when we refresh the page, we retrieve the data render page, and if we don’t refresh it, we don’t retrieve it. This method is still feasible

  • Second: another way to pre-obtain data

    Another method is more advanced, the principle I still do not know, but is good, ha ha, this thing is really evil, why say so, in fact, the essence did not change what, is to change a kind of writing can. So the way I’m going to write this is I’m going to dispatch in getInitalProps an action to fetch data, and as you can see from the last section or from the code, this action is going to fetch an API to fetch data and return state. This is one of the basic processes redux uses to get data. It doesn’t work on a refresh, but it does: Instead of getting the data from the Dispatch Action, get the data from the FETCH API in getIntialProps so that the data can be retrieved each time the page is refreshed… It’s amazing. I don’t know why.

// /pages/user/userList import fetch from 'isomorphic-unfetch'; import UserList from '.. /.. /containers/user/UserList'; import { fetchUserListDataSuccess } from '.. /.. /redux/actions/user'; UserList.getInitialProps = async (props) => { const { store, isServer } = props.ctx; let userData; if (store.getState().user.list.list.length === 0) { const res = await fetch('https://jsonplaceholder.typicode.com/users'); userData = await res.json(); store.dispatch(fetchUserListDataSuccess(userData)); } return { isServer }; }; export default UserList;Copy the code

Is very magical, to tell the truth, I really don’t know why, if there is a Daniel to tell me about it, thank you very much ~

However, I prefer the first of these two methods, because I think the first is under my control, because the react project used to control some data acquisition during its life cycle. Maybe more accustomed, but I admit the second one is better and probably better.

Document

From my perspective, this component serves the same purpose as I did in previous chapters: we don’t have index.html in Next. Js like the index.html in create-react-app. Therefore, there is no way to define the structure of the final rendered HTML, such as the title, meta tags, etc. I started with the HEAD component for Next/Head, but the head component actually ended up generating the HTML head tag. The Document component is all about helping us construct the HTML structure.

// pages/_document.js import Document, {Head, Main, NextScript } from 'next/document'; export default class MyDocument extends Document { static async getInitialProps(ctx) { const initialProps = await Document.getInitialProps(ctx); return { ... initialProps }; } render() { return ( <html> <Head> <meta name='viewport' content='width=device-width, initial-scale=1' /> <meta charSet='utf-8' /> <title>Next-Antd-Scafflod</title> <link rel='shortcut icon' href='/static/favicon.ico' type='image/ico'/> <link rel='stylesheet' href='/_next/static/style.css' /> </Head> <body> <Main /> <NextScript /> </body> </html> ); }}Copy the code

_document.js is only rendered in the Next. Js server. The client just takes the HTML string rendered by the server and renders the front end of the page.

Dynamic Import

In fact, when writing server-side rendering projects, I encountered a lot of pitfalls, the most common is for example, I want to introduce some external components, these components have client variables like Window,document, and so on, but these variables do not exist on the server side, so when rendering on the server side, I will get an error, so it is very troublesome. Various configurations need to be webpack and then introduced asynchronously. For example, a rich text editor. Next directly encapsulates dynamically imported imports for us, using the WebPack import method of course, whatever it is. Loading a rich text editor into a rich text editor and loading another component into a blank space is very simple.

import dynamic from 'next/dynamic'; Const DynamicComponent = dynamic(import(' braft-Editor '), {loading: () => <p> Loading a component... </p> }); </h1> <div style={{width: '50%', height: 0) render() {return (< this.state.username}</h1> <div style={{width: '50%', height: 0) '400px', }}> <DynamicComponent /> </div> </Fragment> ); }Copy the code

There are four asynchronous import methods, including importing only the document address on the server side

error handling

Error handling. A lot of good scaffolding provides error handling, such as 404 and 500 page rendering. Next. That means we don’t actually have to do anything to enjoy this service. For example, if I enter a random url into the system, the following result will appear:

You can create a new _error.js file in the pages folder, and write your errorPage code in it

// /pages/_error.js
import React from 'react'

export default class Error extends React.Component {
  static getInitialProps({ res, err }) {
    const statusCode = res ? res.statusCode : err ? err.statusCode : null;
    return { statusCode }
  }

  render() {
    return (
      <p>
        {this.props.statusCode
          ? `An error ${this.props.statusCode} occurred on server`
          : 'An error occurred on client'}
      </p>
    )
  }
}

Copy the code

Ok, so as you can see, it’s clearly working. Although the effect is similar, but if you follow your own writing, it will be no problem. Ha ha ~

Static HTML export

Another advanced function, it supports us to export various routes into static pages, but if you think about it, it is not much use, after all, our projects are logical, export static pages can not operate, haha. But since it’s a pretty awesome feature, I’ll give it a try.

  • First, configure the pages and routes in the Config folder
exportPathMap: async (defaultPathMap) => {
    return {
      '/home': { page: '/' },
      '/userList': { page: '/user/userList' },
    }
  },
Copy the code
  • Second, package.json adds the export command
"scripts": { ... // Add export command "export": "yarn build && next export"},Copy the code
  • Step 3 run the yarn export command

    After running the command, an Out folder appears in the root directory, which is really magic, with the pages folder and the necessary static resources.

Then we’ll open up index.html and go to what should be our home page, which looks something like this.emm… This home page is a bit strange, static resources and CSS are not quite right, as for why I don’t go to pursue, there must be a way. But I just try the function, time is limited ready to rest, ha ha. If you’re interested, do your own research.

There is also an advanced function to push the Next. Js project to github page, which also depends on the export. However, I did not write the time problem, please check the official demo, it should be solved ~

conclusion

That concludes the Next. Js Trampling primer series. Thank you very much for many friends who have been watching, there are some lovely friends to urge more, the level is limited, it is a collection of trampling pits, if I can help you really happy. Thank you for reading. Next, I’m going to build a website with next.js. After that, I will probably write another article about the Next. Js website, but I won’t write anything else

Project code address, give a Star if you like, thank You Mina Sang