Help ssr, use concent to add some material to the nextjs application
幻魂
Posted on December 21, 2020
Open source is not easy, thank you for your support, ❤ star concent^_^
Preface
nextjs
is a very popular React server-side rendering application framework. It is very lightweight, easy to use, and has an active community. So when we use react
to write an application that requires ssr
(server side render), it is basically The city’s first choice is nextjs
. concent
is a new generation of react
state management solution. It has a built-in dependency collection system. It also has the characteristics of 0 intrusion, predictability, gradual and high performance, and provides lifecyle
, composition api
and other flexible APIs are super simple to write, allowing you to easily control ultra-large-scale react applications.
Hello next
Here we will use the create-next-app
command to install a basic next sample application
npx create-next-app hello-next
After execution, you can see a directory structure as follows
|____public
|____pages
| |____ _app.js // The default root component of the next application
| |____index.js // Default homepage
| |____api // api routing file
| | |____hello.js
After we execute npm run dev
in the project root directory, we will see a default homepage of ssr
driven by next
Hello concent
Here we will use the create-react-app
command to install a basic concent sample application
npx create-react-app hello-concent --template concent-ts
After execution, you can see a directory structure as follows
|____index.tsx
|____App.tsx
|____types // store type definition
|____features // List of functional components
| |____counter // counter function
| | |____Counter.tsx // counter component
| | |____model // counter model (including state, reducer, computed)
|____models // Other global model definitions
|____configs
Enter the project directory and execute npm i
, and then execute npm start
to see a default counter page
You can also click here to understand and edit it online.
Of course, integrating concent
in an existing project is also super simple, because it does not need to provide a Provider
at the top level, just configure the model in advance.
import {run} from'concent';
run({ // Define a counter model
counter: {
state: {num: 1, bigNum: 10 },
reducer: {
add(payload, moduleState) {
return {num: moduleState + 1 };
},
async asyncAddBig() {
await new Promise(resolve => setTimeout(resolve, 1000));
return {bigNum: moduleState + 10 };
}
},
computed: {
doubleNum: ({ num }) => num * 2, // This function is triggered only when num changes
}
}
})
After that, you can plug and play globally. Both class components and function components can use the same way to read data or call methods, click on the key point, *if the ui is a conditional statement to control whether to consume state or derived data If it is, it is recommended to write delayed deconstruction, so that the minimum granularity of the view to the data collected after each round of rendering *
// ###### Function component
function Demo(){
// If state and moduleComputed are read on demand, it is recommended to write delayed deconstruction
const {state: {num, numBig }, moduleComputed: {doubleNum }, mr} = useConcent('counter');
// ... ui logic, binding data, binding method
}
// ###### Class component
const DemoCls = register('counter')(
class DemoCls extends React.Component{
render(){
const {state: {num, numBig }, moduleComputed: {doubleNum }, mr} = this.ctx;
// ... ui logic, binding data, binding method
}
}
)
Introduce concent in next
There is a _app.js
file in the next basic example directory, which is the root component of the next application
import'../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Because the model must be configured in advance before using concent
, we only need to create a runConcent.js
file in advance
import {run} from'concent'
import * as models from'./models';
run(models);
Then import it in the _app.js
file, so that all sub-components under the root component can correctly obtain the store's data and mobilize the store's method.
import'../styles/globals.css'
+ import'./runConcent'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
Then we create a counter.js
file in the next pages directory, representing that this is a page component, so that the browser can use the /counter
route to access the rendering view of this component.
import React from'react'
import {useConcent} from'concent'
import router from'next/router'
// use next/router to do browser side router jump
function toHomePage(){
router.push('/');
}
export default function Counter() {
const {state, mr, moduleComputed} = useConcent('home')
return (
<div>
this is counter page
<h1>num: {state.num}</h1>
<h1>doubleNum: {moduleComputed.doubleNum}</h1>
<button onClick={mr.add}>add</button>
<button onClick={toHomePage}>to home page</button>
</div>
);
}
That's it, a next
application with concent
is created. Isn't it particularly simple? ^_^
Support pre-rendering
next
provides two levels of pre-rendering interfaces, namely getServerSideProps
and getStaticProps
. The difference between the two is the execution timing. getServerSideProps
is executed every time a page is requested, while getStaticProps
is executed during construction. Let's deal with the situation of getServerSideProps
first, and see how to combine concent
for pre-rendering support.
First of all, we do not consider the existence of concent
. To do pre-rendering support in next
, we only need to expose a getServerSideProps
interface in your page component.
// This function is called every time a page change is requested
export async function getServerSideProps() {
// Call external API to get the list of blog posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// By returning {props: posts} object, the PostPage component will receive the `posts` parameter when rendering
return {
props: {posts },
}
}
function PostPage({ posts }) {// The posts parameter is received here
// Render posts...
}
export default PostPage
The reason why Blog
can receive posts
, in addition to exposing the interface of getServerSideProps
, let us observe the content of the root component file of _app.js
, and we can find the key points!
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp
The pageProps
in the parameter list is the object pointed to by the props
in the return result of getServerSideProps
, and then next
transmits it to the target page component, so we can deconstruct it in the PostPage
parameter list. posts
.
So our entry point can start from here, we put the return result of getStaticProps into a format constraint, like a structure like {module:string, state: object}
, and then record it in the _app.js
file Go to the store
// This function is called on every request
export async function getServerSideProps() {
// Call external API to get the list of blog posts
await delay();
const posts = [
{id: 1, name:'post1 -----' },
{id: 2, name:'post2 --- welcome to use concent' },
];
// This returned object will be transparently transmitted to the pageProps of the root component, where the module and the state entity object to which the state belongs are returned
// record the status to the store there
return {
props: {
module:'test',
state: {posts },
}
};
}
The root component file at this time is changed as follows
import'../styles/globals.css';
+ import'./runConcent';
+ import {setState} from'concent';
function MyApp({ Component, pageProps }) {
// Record the return status of getServerSideProps to the corresponding module of store here
+ if (pageProps.module) {
+ setState(pageProps.module, pageProps.state);
+}
return <Component {...pageProps} />
}
export default MyApp;
Then we implemented the page component post-page
code as follows
const PostList = React.memo(function () {
const {state} = useConcent('test');
return (
<div>
{state.posts.map(item => <h3 key={item.id}>{item.name}</h3>)}
</div>
);
});
const PostLength = React.memo(function () {
const {state} = useConcent('test');
return <h1>{state.posts.length}</h1>;
});
export default function PostPage() {
return (
<div>
<h1>this is post page</h1>
<PostList />
<PostLength />
<button onClick={toHomePage}>to home page</button>
</div>
);
}
Then we open the browser to visit the /post-page
page, click to view the source code and you will see that this is a server-side pre-rendered page
For the same reason, we can also replace getServerSideProps
with getStaticProps
, the whole process above will still work normally, you are welcome to see the clone sample code to experience it yourself.
git clone https://github.com/concentjs/ssr-demo-1
Appendix
doc
CloudBase CMS
Welcome brothers to pick up CloudBase CMS to create a one-stop cloud content management system, which is developed by the cloud and based on Node.js Headless The content management platform provides a wealth of content management functions, is simple to install, easy for secondary development, and is closely integrated with the cloud development ecosystem to help developers improve development efficiency.
concent
has provided strong support for its management background, and the new version of the management interface is more beautiful and considerate.
FFCreator
You are also welcome to pick up FFCreator, it is a lightweight and flexible short video processing library based on node.js. You only need to add a few pictures or video clips and a background music, you can quickly generate a cool video clip.
FFCreator
is a lightweight and simple solution, it only requires few dependencies and low machine configuration to quickly start working. And it simulates 90% of the animation effects of animate.css. You can easily convert the animation effects on the web page side into videos, which is really awesome.
Posted on December 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.