It is nothing new to use Nextjs to develop applications. Recently, the project also decided to use it to revise our website for SEO optimization. After one or two weeks of study and thinking, I have gained new insights on store management in Nextjs.
This is the 9th day of my participation in the August Wen Challenge.More challenges in August
State management
Our state management library uses Mobxjs, which is a very handy library with a clever, straightforward, responsive approach that supports an object-oriented programming paradigm. The decision to adopt the library came after lengthy discussions, balancing between Redux and Mobx, and Mobx won because of some of its features.
There is no problem at all when the application only renders on the client side. However, when the application is also running on the server, many problems arise:
- How do we render HTML on the server and return it to the front end
- When we render the HTML, how do we sync the corresponding store to the front end
- How is store represented on the server side?
In fact, the above three questions are closely connected, answer the third question, the rest will be solved.
So what does store look like on the server side?
Store is responsible for providing data support during server-side rendering, before the HTML content is actually generated. If the language state of the application is stored in the Store, the HTML for a request may need either Chinese or English, depending on the user’s previous configuration or Settings. So we need to initialize the Store to what we need before we start rendering the HTML: the store needs to expose an initialization method.
As we continue our analysis, since the requests are independent of each other, we must create and initialize a new store on each request to avoid data collisions.
In addition, store only provides data support for the requested page, and there is no UI interaction or secondary change of state. Therefore, Store is just an ordinary JS object on the server side:
Graph TD Request --> Store initialization --> Read Store data to complete page rendering --> clientSide
So, let’s summarize store’s performance on the server side:
- Creates a store tree frequently (each request creates a new store tree, which js garbage collection takes care of)
- We don’t need the responsiveness of the Store (the client does)
- There is a risk of memory leaks
For the above points, we need a large memory server to support server-side rendering, and we need to disable Mobx’s responsiveness on the server side:
import {enableStaticRendering} from 'mobx-react'
if(! progress.browser){ enableStaticRendering(true)}Copy the code
In addition, we need to provide an initialization method in the store:
export class Store {
// omit the store implementation
}
let store:Store;
export function initializeStore(initialState:InitialStore = {}) {
if (isServer) {
return new Store(initialState)
}
if(! store) { store =new Store(initialState)
}
return store
}
Copy the code
Data synchronization
Having solved the problem of store performance on both the server and client sides, the next practical problem is:
After we initialize the store on the server side, we render the HTML according to the store and return to the front end. How does the front end store synchronize with the server side store?
Analysis shows that there are two steps:
- The store data on the server is transmitted to the front-end
- The front end initializes the store based on the passed data
The first problem is found when we open the console:
What we found was that Nextjs cleverly serialized the server’s store data into JSON and inserted it into the script so that the front-end could retrieve the data.
Second question, how does the front end use this data? Remember our store initialization method initializeStore? We just feed it data.
const MyApp = ({ Component, pageProps,initialStore }: AppProps) = > {
// The server renders directly using the store passed below, and the client initializes a new store based on the data passed by the server (reactive).
const mobxStore = process.browser ? initializeStore(initialStore):initialStore;
return (
<StoreContext.Provider value={mobxStore}>
<AuthProvider>
<Component {. pageProps} / >
</AuthProvider>
</StoreContext.Provider>
);
};
MyApp.getInitialProps = async (appContext:any) => {
// The server initializes the store. Here we initialize the language to English, or we can initialize it based on external data
const initialStore = initializeStore({
sharedStore: {lang:"English"}})const appProps = await App.getInitialProps(appContext);
return { ...appProps,initialStore };
}
Copy the code
thanks
In fact, I’ve read a lot of articles recently, including one by Ali. This is probably a best practice for data flow in a large complex project, and it shows a better way to organize the store. Next. Js is a complete guide to deploying Web isomorphism (MobX + TypeScript). Thank you for your selfless sharing.
Finally, thanks for reading. If you have any questions, please leave a comment.