初探React Hooks & SSR改造

Hooks

React v16.8 发布了 Hooks,其主要是解决跨组件、组件复用的状态管理问题。

在 class 中组件的状态封装在对象中,然后通过单向数据流来组织组件间的状态交互。这种模式下,跨组件的状态管理变得非常困难,复用的组件也会因为要兼容不同的组件变得产生很多副作用,如果对组件再次拆分,也会造成冗余代码增多,和组件过多带来的问题。

后来有了 Redux 之类的状态管理库,来统一管理组件状态。但是这种分层依然会让代码变得很复杂,需要更多的嵌套、状态和方法,写代码时也常在几个文件之间不停切换。hooks 就是为了解决以上这些问题。

文章不对 hooks 做太多详细介绍,建议阅读此文前,先到官网做大概的了解。此文基于上一篇文章《实现ssr服务端渲染》的代码,进行 hooks 改造。代码已经提交到仓库的 hooks 分支中,仓库链接 https://github.com/zimv/react-ssr/tree/hooks

面向对象编程和函数式编程

在了解 hooks 的过程中,慢慢的感觉到了面向对象和函数式编程的区别。

在 class 模式中状态和属性方法等被封装在组件内,组件之间是相互以完整对象个体做交互,状态的修改需要在对象内部的 setState 中处理。

而 hooks 模式中,一切皆函数,也就是 hooks,可以被拆分成很多小单元再进行组合,修改状态的是一个 set 方法,此方法可以在任何其他的 hooks 中出现和调用。 class 更属于面向对象编程,而 hooks 更属于函数式编程。

React 也并不会移除 class,而是引入 hooks 使开发者能根据场景做更好的选择。它们依旧会在未来保持应有的迭代。

变化

使用 hooks 之后,原本的生命周期概念就会有所变化了。比如我们定义一个 hooks 组件 Index, 当组件运行时,Index 函数的调用就是一次 render,那么我们第一次 render 相当于原来的 willMount,而 useEffect 会在第一次 render 以后执行。官网文档也说过你可以把 useEffect Hooks 视作 componentDidMountcomponentDidUpdate 和 componentWillUnmount 的结合。 state 也被 useState 替代,useState 传入初始值并返回变量和修改变量的 set 方法。

在我们服务端渲染的时候,上篇文章说过生命周期只会执行到 willMount 后的第一次 render。  那在我们 hooks 模式下,服务端渲染会执行 Index hooks 第一次 render,而 useEffect 不会被执行。

function Index(props){
    console.log(‘render‘);
    const [desc, setDesc] = useState("惹不起");
    useEffect(() => {
        console.log(‘effect‘)
    })
    return (<div>{desc}</div>)
}

useEffect

如果使用了 useEffect,组件钩子每次 render 以后,useEffect 会被执行。 useEffect 第一个入参是需要调用的方法,方法可以返回一个方法,返回的方法会在非首次执行此 useEffect 之前调用,也会在组件卸载时调用。

第二个参数是传入一个数组,是用来限制 useEffect 执行次数的,如果不传入此参数,useEffect 会在每次 render 时执行。如果传入第二个数组参数,在非首次执行 useEffect 时,数组中的变量较上一次 render 发生了变化,才会再次触发 useEffect 执行。

看如下代码,当页面首次 render ,useEffect 执行异步数据获取,当数据获取成功,setList 设置值以后(类似 setState 会触发 render ),会再次执行 render,而 useEffect 还会再次执行,数据请求结束以后,setList 又会导致 render,因此陷入死循环。

const [list, setList] = useState([]);
useEffect(() => {
    API.getData().then(data=>{
        if (data) {
            setList(data.list);
        }
    });
});

所以需要使用第二个参数,限制执行次数,我们传入一个 1,就可以实现仅执行一次 useEffect 。当然也可以通过传入一个 useState 变量。

const [list, setList] = useState([]);
useEffect(() => {
    API.getData().then(data=>{
        if (data) {
            setList(data.list);
        }
    });
}, [1]);

class 改造

在原本的 SSR 仓库的前提下,仅针对组件部分,进行 hooks 改造。首先回顾 getInitialProps 在 class 模式下,是在 class 写一个 static 静态方法,如下:

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
    };
  }
}

在 hooks 中,class 变成了普通函数,以前的继承变得没有必要也无法适应需求,因此 getInitialProps 直接写在函数的属性中,方法本身返回的数据格式依然不变,返回一个对象。如下:

function Index(props) {
}
Index.getInitialProps = async () => {
  let data;
  const res = await request.get("/api/getData");
  if (!res.errCode) data = res.data;
  return {
    data
  };
};

包括定义的网页 title,之前也是使用 static,现在我们也 Index.title = ‘index‘  这样定义。

hooks 规范要求如下,援引中文 React 文档:

  • 只能在顶层调用钩子。不要在循环,控制流和嵌套的函数中调用钩子。
  • 只能从 React 的函数式组件中调用钩子。不要在常规的 JavaScript 函数中调用钩子。(此外,你也可以在你的自定义钩子中调用钩子。)

在 class 模式下,我们继承了 Base,Base 会定义 constructor 和 componentWillMount 来处理 state 和 props ,可以帮助我们解决服务端渲染和客户端渲染下初始化状态数据的赋值和获取,因此我们才可以统一一套代码在客户端和服务端中运行(如需要,查看上篇文章了解详情)。

class 模式下的继承 Base 属于面向对象编程模式,而 hooks 模式下,由于需要在函数内使用 useState 来定义状态,并且返回方法来设置状态,这样看起来更偏向函数式编程,在这种场景下,继承变得不适应。因此需要对 Base 进行改造,在 Base 编写 hooks,在页面组件 hooks 中使用。

在 class 模式下,我们使用继承 Base 来处理 state 和 props,由于 Base 已经封装了 constructor 和 componentWillMount 处理 state 和 props,因此我们只需要定义好静态 state 和 getInitialProps,组件便会自动处理相关逻辑,大致使用代码如下

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
    };
  }
}

在 hooks 模式下不一样,因为摒弃了继承,需要用 Base 自定义 hooks,然后在页面组件中使用。Base 中的 getProps 和 requestInitialData 钩子调用时,需要传入当前 Index 组件的部分对象,然后在 Base hooks 中返回变量初始值或者调用 set 修改当前 hooks 中的状态值,大致使用如下:

import { getProps, requestInitialData } from "../base";

function Index(props) {
  const [desc, setDesc] = useState("Hello world~");
  //getProps获取props中的ssrData,重构和服务端渲染时props有值,第三个参数为默认值
  const [data, setData] = useState(getProps(props, "data", ""));

  //在单页面路由页面跳转,渲染组件时,requestInitialData调用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;

如此封装以后,我们依然保证了一套代码能在服务端和客户端运行,requestInitialData 方法第三个传入参数,是一个对象,传入了需要被修改的状态的 set 方法,最终 getInitialProps 返回数据后,会和传入的对象对比,属性名一致便会调用 set 方法进行状态修改,requestInitialData 是一个 useEffect hook,代码如下

export function requestInitialData(props, component, setFunctions) {
  useEffect(() => {
    //客户端运行时
    if (typeof window != "undefined") {
      //非同构时,并且getInitialProps存在
      if (!props.ssrData && component.getInitialProps) {
        component.getInitialProps().then(data => {
          if (data) {
            //遍历结果,执行set赋值
            for (let key in setFunctions) {
              for (let dataKey in data) {
                if (key == dataKey) {
                  setFunctions[key](data[dataKey]);
                  break;
                }
              }
            }
          }
        });
      }
    }
  },[1]);
}

至此,针对我之前的 SSR 代码,就完成了 hooks 的改造。React hooks 的改造非常平滑,class 和 hooks 混用也不会造成什么问题,如果需要在旧的项目中使用 hooks 或者对原有的 class 进行改造,完全可以慢慢的一部分一部分迭代。当然 React Hooks 还有 useContext useReducer 等,不妨现在就去试试 Hooks ?

关联文章:《 实现ssr服务端渲染

关联仓库: https://github.com/zimv/react-ssr/tree/hooks

分享一个朋友的人工智能教程。零基础!通俗易懂!风趣幽默!段子多!大家可以看看是否对自己有帮助【点击前往】

原文地址:https://www.cnblogs.com/1wen/p/10600816.html

时间: 2024-11-08 21:23:34

初探React Hooks & SSR改造的相关文章

React Hooks用法大全

前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染.在我看来,使用 React Hooks 相比于从前的类组件有以下几点好处: 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便

关于react hooks

一.react的组件 react的核心是组件,react有两种组件类:有状态组件(class)和无状态组件(function). 有状态组件(class)常常使代码变的冗余而复杂,例如下面一个简单的button组件: import React, { Component } from "react"; export default class Button extends Component { constructor() { super(); this.state = { button

理解 React Hooks

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classes components)的 state 和 组件生命周期,而不需要在 mixin. 函数组件.HOC组件和 render props 之间来回切换,使得函数组件的功能更加实在,更加方便我们在业务中实现业务逻辑代码的分离和组件的复用. 本文将从以下几个方面介绍 hooks Hooks 在解决什么问题

30分钟精通React今年最劲爆的新特性——React Hooks

你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? --拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function. 你还在为搞不清使用哪个生命周期钩子函数而日夜难眠吗? --拥有了Hooks,生命周期钩子函数可以先丢一边了. 你在还在为组件中的this指向而晕头转向吗? --既然Class都丢掉了,哪里还有this?你的人生第一次不再需要面对this. 这样看来,说React Hooks是今年最劲爆的新特性真的毫不夸张.如果你也对react

react新特性 react hooks

本文介绍的是react新特性react hooks,本文面向的是有一定react开发经验的小伙伴,如果你对react还不是很熟悉的话我建议你先学习react并多多联系. 首先我们都知道react有3种组件:分别是Function函数式无状态组件.class有状态组件.高阶组件.这里不对这3种组件做一一介绍. 本文重点是react hooks 一个最简单的Hooks 首先让我们看一下一个简单的有状态组件: 1 class Example extends React.Component { 2 co

通过 React Hooks 声明式地使用 setInterval

本文由云+社区发表 作者:Dan Abramov 接触 React Hooks 一定时间的你,也许会碰到一个神奇的问题: setInterval 用起来没你想的简单. Ryan Florence 在他的推文里面说到: 不少朋友跟我提起,setInterval 和 hooks 一起用的时候,有种蛋蛋的忧伤. 老实说,这些朋友也不是胡扯.刚开始接触 Hooks 的时候,确实还挺让人疑惑的. 但我认为谈不上 Hooks 的毛病,而是 React 编程模型和 setInterval 之间的一种模式差异.

React Hooks简单业务场景实战(非源码解读)

前言 React Hooks 是React 16.7.0-alpha 版本推出的新特性.从 16.8.0 开始,React更稳定的支持了这一新特性. 它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 注意:React 16.8.0 是第一个支持 Hook 的版本.升级时,请注意更新所有的 package,包括 React DOM.React Native 将在下一个稳定版本中支持 Hook. 如果说promise是JavaScript异步的终极解决方案,那

react hooks学习

接触React项目快两个月了,还在研究摸索各种知识点的过程中,充实且幸福. 在项目中学习新知识,还是很有效率的,一边写项目,一边实验新的知识点,比如react hooks!嘻嘻嘻~~~ 写了好一段时间class组件了,想尝试尝试函数式组件,之前也有试过,但是一碰到需要使用state的地方,只能又把function改成了class,心塞塞,然后没事刷博客,看到了react hooks,有一种缺什么,就有什么新知识冒出来的感觉. 1.State Hook,使用state import { useSt

React Hooks究竟是什么呢?

摘要: React Hooks原理解析. 原文:快速了解 React Hooks 原理 译者:前端小智 我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件却不能? React 早期版本,类组件可以通过继承PureComponent来优化一些不必要的渲染,相对于函数组件,React 官网没有提供对应的方法来缓存函数组件以减少一些不必要的渲染,直接 16.6 出来的 React.memo函数. React 16.8 新出来的Hook可以让React 函数