Hooks
React V16.8 releases Hooks, which address state management across components and component reuse.
The state of a component in a class is encapsulated in an object, and state interactions between components are organized through one-way data flows. In this mode, state management across components becomes very difficult, and reusable components will have many side effects due to compatibility with different components. If components are split again, there will be more redundant code, and the problem of too many components.
Later, state management libraries such as Redux were developed to centrally manage component state. But this layering still makes the code more complex, requiring more nesting, states, and methods, and often switching between several files when writing code. Hooks are designed to solve these problems.
I don’t want to go into too much detail about hooks, but I recommend that you check out the hooks website before reading this article. This article is based on hooks from the previous article implementing SSR server Rendering. The code has been committed to the hooks branch of the repository, linked to github.com/zimv/react-… .
Object-oriented programming and functional programming
In learning about hooks, I began to feel the difference between object-oriented and functional programming.
In class mode, state and property methods are encapsulated in components, and components interact with each other as whole objects. State modification needs to be handled in setState inside the object.
In hooks mode, everything functions, which are hooks, can be broken into small units and combined, and the state is changed by a set method that can appear and be called from any other hooks. Class is more object-oriented; hooks are more functional.
React also does not remove classes, but introduces hooks that allow developers to make better choices based on the scenario. They will continue to iterate in the future.
change
The original concept of the life cycle is changed by using hooks. For example, if we define a hooks component called Index, when the component runs, the Index call is a render, the first render is willMount, and useEffect is executed after the first render. The useEffectHooks documentation also says you can think of useEffectHooks as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount. State is also replaced by useState, which passes in the initial value and returns the variable and the set method that modifies it.
When we render on the server, the previous article stated that the life cycle only executes to the first render after willMount. In our hooks mode, server render will execute Index hooks first render, useEffect will not be executed.
function Index(props){
console.log('render');
const [desc, setDesc] = useState("I can't afford to mess around.");
useEffect(() => {
console.log('effect')})return (<div>{desc}</div>)
}Copy the code
useEffect
If useEffect is used, useEffect is executed after each render of the component hook. UseEffect The first entry is the method that needs to be called. A method can return a method that will be called either before the useEffect is first executed or when the component is unloaded.
The second argument is passed in an array that limits the number of times useEffect can be executed. Otherwise, useEffect will be executed on each render. If a second array argument is passed, useEffect execution will not be triggered again until the variables in the array have changed since the last render execution.
When the page first render, useEffect performs the asynchronous data retrieval. When the data is retrieved successfully, setList is set to render and useEffect is executed again. After the data request ends, setList leads to render again, so it’s in an endless loop.
const [list, setList] = useState([]);
useEffect(() => {
API.getData().then(data=>{
if (data) {
setList(data.list); }}); });Copy the code
Therefore, we need to use the second parameter to limit the number of executions. We pass in a 1 to implement useEffect only once. You can also pass in a useState variable.
const [list, setList] = useState([]);
useEffect(() => {
API.getData().then(data=>{
if (data) {
setList(data.list); }}); }, [1]);Copy the code
The class reform
On the premise of the original SSR warehouse, only for the components, hooks are reformed. GetInitialProps () {class props ();
export default class Index extends Base {
static async getInitialProps() {
let data;
const res = await request.get("/api/getData");
if(! res.errCode) data = res.data;return{ data }; }}Copy the code
In hooks, the class becomes a normal function, and the previous inheritance becomes unnecessary and unadaptable, so getInitialProps is written directly to the properties of the function, and the data format returned by the method itself remains the same, returning an object. As follows:
function Index(props) {
}
Index.getInitialProps = async () => {
let data;
const res = await request.get("/api/getData");
if(! res.errCode) data = res.data;return {
data
};
};Copy the code
Title = ‘Index’; title = ‘Index’;
The hooks specification requires the following:
- Hooks can only be called at the top level. Do not call hooks in loops, control flows, and nested functions.
- You can only call hooks from functional components of React. Do not call hooks in regular JavaScript functions. (In addition, you can also call hooks from your custom hooks.)
In class mode, we inherit Base, which defines constructor and componentWillMount to handle state and props. Base defines constructor and componentWillMount to handle state and props, which helps us solve the assignment and retrieval of initialization state data for server rendering and client rendering. This is why we can unify a single set of code to run on both the client and server (see the previous article for details if necessary).
Inheritance Base in class mode is object-oriented programming mode, whereas in hooks mode, which looks more functional because you need to use useState in functions to define state and return methods to set state, inheritance is not suitable in this scenario. So you need to modify Base to write hooks in Base and use them in the page component hooks.
In class mode, we use an inherited Base to handle state and props. Since Base already encapsulates constructor and componentWillMount to handle state and props, So all we need to do is define static state and getInitialProps, and the component will handle the logic automatically, roughly as follows
export default class Index extends Base {
static state = {
desc: "Hello world~"
};
static async getInitialProps() {
let data;
const res = await request.get("/api/getData");
if(! res.errCode) data = res.data;return{ data }; }}Copy the code
This is not the case in hooks mode, because inheritance is discarded and custom hooks are used with Base and then used in page components. To call the getProps and requestInitialData hooks in Base, you need to pass in part of the object of the current Index component, and then either return the initial value of the variable in Base hooks or call SET to change the state in Base hooks. The general usage is as follows:
import { getProps, requestInitialData } from ".. /base";
function Index(props) {
const [desc, setDesc] = useState("Hello world~"); //getProps gets props ssrData, props has a value when refactoring or server rendering, and the third parameter is the default const [data,setData] = useState(getProps(props, "data"."")); // requestInitialData calls getInitialProps requestInitialData(props, Index, {data:setData });
return (<div>{data}</div>)
}
Index.getInitialProps = async () => {
let data;
const res = await request.get("/api/getData");
if(! res.errCode) data = res.data;return {
data
};
};
export default Index;Copy the code
The third parameter to the requestInitialData method is an object that passes the set method of the state that needs to be modified. Finally, when getInitialProps returns the data, it compares it to the object. RequestInitialData is a useEffect hook with the following code
export function requestInitialData(props, component, setFunctions) {useEffect(() => {// Client run timeif(typeof window ! ="undefined") {// Non-isomorphic, and getInitialProps existsif(! props.ssrData && component.getInitialProps) { component.getInitialProps().then(data => {if(data) {// Iterate the result, executesetThe assignmentfor (let key in setFunctions) {
for (let dataKey in data) {
if (key == dataKey) {
setFunctions[key](data[dataKey]);
break; }}}}}); }}}, [1]); }Copy the code
So far, I have completed the hooks modification for my previous SSR code. The React hooks fix is very smooth, and mixing class and hooks doesn’t cause any problems. If you need to use hooks on an older project or modify an existing class, you can iterate slowly, piece by piece. React-hooks are react-hooks. Use the useContext useReducer.
Related article: “Implementing SSR Server Rendering”
Associated warehouse: github.com/zimv/react-…