关于为什么使用React新特性Hook的一些实践与浅见

前言

关于Hook的定义官方文档是这么说的:

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

简单来说,就是在使用函数式组件时能用上state,还有一些生命周期函数等其他的特性。

如果想了解Hook怎么用,官方文档和阮一峰的React Hooks 入门教程都讲得很清楚了,我建议直接看官方文档和阮大神的文章即可。

本篇博客只讲为什么要用React的Hook新特性,以及它解决了什么问题。

为什么使用Hook?

让我们先看看别人怎么说。

阮大神的文章中给了一个示例代码:

import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}

并且提出:

这个组件类仅仅是一个按钮,但可以看到,它的代码已经很"重"了。
真实的 React App 由多个类按照层级,一层层构成,复杂度成倍增长。
再加入 Redux,就变得更复杂。

实际上,上面这个代码的“重”有部分来源于写法问题,他可能并没有“重”,让我们看看下面这种class写法:

import React, { Component } from "react";

export default class Button extends Component {
  state = {
    buttonText: "Click me, please"
  }
  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}

然后再对比下使用了Hook的函数式组件:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me,   please");

  function handleClick() {
    return setButtonText("Thanks, been clicked!");
  }

  return <button onClick={handleClick}>{buttonText}</button>;
}

即使是我们简化过的class写法,比起Hook的看起来好像也确实“重”了点。

Hook的语法确实简练了一些,但是这个理由并不是那么充分。

阮大神同时列举了Redux 的作者 Dan Abramov 总结了组件类的几个缺点:

  • 大型组件很难拆分和重构,也很难测试。
  • 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。(这里我认为阮大神写的可能有点问题,应该是是各个生命周期方法更为准确)
  • 组件类引入了复杂的编程模式,比如 render props 和高阶组件。

这三点都是事实,于是有了函数化的组件,但之前的函数化组件没有state和生命周期,有了Hook那么就可以解决这个痛点。

而且Hook并不只是这么简单,通过自定义Hook,我们可以将原有组件的逻辑提取出来实现复用。

用useEffect解决生命周期导致的重复逻辑或关联逻辑

上面举的几个缺点,第一点和第三点你可能很容易理解,第二点就不容易理解了,所以我们需要深入到具体的代码中去理解这句话。

我们看看下面这段代码:

import React, { Component } from "react";

export default class Match extends Component {
  state={
    matchInfo:''
  }
  componentDidMount() {
    this.getMatchInfo(this.props.matchId)
  }
  componentDidUpdate(prevProps) {
    if (prevProps.matchId !== this.props.matchId) {
      this.getMatchInfo(this.props.matchId)
    }
  }
  getMatchInfo = (matchId) => {
    // 请求后台接口获取赛事信息
    // ...
    this.setState({
      matchInfo:serverResult // serverResult是后台接口的返回值
    })
  }

  render() {
    const { matchInfo } = this.state
    return <div>{matchInfo}</div>;
  }
}

这样的代码在我们的业务中经常会出现,通过修改传入赛事组件的ID,去改变这个赛事组件的信息。

在上面的代码中,受生命周期影响,我们需要在加载完毕和Id更新时都写上重复的逻辑和关联逻辑。

所以现在你应该比较好理解这句话:业务逻辑分散在组件的各个生命周期方法之中,导致重复逻辑或关联逻辑

为了解决这一点,React提供了useEffect这个钩子。

但是在讲这个之前,我们需要先了解到React带来的一个新的思想:同步。

我们在上面的代码中所做的实际上就是在把组件内的状态和组件外的状态进行同步。

所以在使用Hook之前,我们需要先摒弃生命周期的思想,而用同步的思想去思考这个问题。

现在再让我们看看改造后的代码:

import React, { Component } from "react";

export default function Match({matchId}) {
  const [ matchInfo, setMatchInfo ] = React.useState('')
  React.useEffect(() => {
    // 请求后台接口获取赛事信息
    // ...
    setMatchInfo(serverResult) // serverResult是后台接口的返回值
  }, [matchId])

  return <div>{matchInfo}</div>;
}

看到这个代码,再对比上面的代码,你心中第一反应应该就是:简单。

React.useEffect接受两个参数,第一个参数是Effect函数,第二个参数是一个数组。

组件加载的时候,执行Effect函数。

组件更新会去判断数组中的各个值是否变动,如果不变,那么不会执行Effect函数。

而如果不传第二个参数,那么无论加载还是更新,都会执行Effect函数。

顺便提一句,这里有组件加载和更新的生命周期的概念了,那么也应该是有组件卸载的概念的:

import React, { Component } from "react";

export default function Match({matchId}) {
  const [ matchInfo, setMatchInfo ] = React.useState('')
  React.useEffect(() => {
    // 请求后台接口获取赛事信息
    // ...
    setMatchInfo(serverResult) // serverResult是后台接口的返回值

    return ()=>{
      // 组件卸载后的执行代码
    }
  }, [matchId])

  return <div>{matchInfo}</div>;
}
}

这个常用于事件绑定解绑之类的。

用自定义Hook解决高阶组件

React的高阶组件是用来提炼重复逻辑的组件工厂,简单一点来说就是个函数,输入参数为组件A,输出的是带有某逻辑的组件A+。

回想一下上面的Match组件,假如这个组件是页面A的首页头部用来展示赛事信息,然后现在页面B的侧边栏也需要展示赛事信息。

问题就在于页面A的这块UI需要用div,而页面B侧边栏的这块UI需要用到span。

保证今天早点下班的做法是复制A页面的代码到页面B,然后改下render的UI即可。

保证以后早点下班的做法是使用高阶组件,请看下面的代码:

import React from "react";

function hocMatch(Component) {
  return class Match React.Component {
    componentDidMount() {
      this.getMatchInfo(this.props.matchId)
    }
    componentDidUpdate(prevProps) {
      if (prevProps.matchId !== this.props.matchId) {
        this.getMatchInfo(this.props.matchId)
      }
    }
    getMatchInfo = (matchId) => {
      // 请求后台接口获取赛事信息
    }
    render () {
      return (
        <Component {...this.props} />
      )
    }
  }
}

const MatchDiv=hocMatch(DivUIComponent)
const MatchSpan=hocMatch(SpanUIComponent)

<MatchDiv matchId={1} matchInfo={matchInfo} />
<MatchSpan matchId={1} matchInfo={matchInfo} />

但是实际上有的时候我们的高阶组件可能会更复杂,比如react-redux的connect,这就是高阶组件的复杂化使用方式。

又比如:

hocPage(
  hocMatch(
    hocDiv(DivComponent)
  )
)

毫无疑问高阶组件能让我们复用很多逻辑,但是过于复杂的高阶组件会让之后的维护者望而却步。

而Hook的玩法是使用自定义Hook去提炼这些逻辑,首先看看我们之前使用了Hook的函数式组件:

import React, { Component } from "react";

export default function Match({matchId}) {
  const [ matchInfo, setMatchInfo ] = React.useState('')
  React.useEffect(() => {
    // 请求后台接口获取赛事信息
    // ...
    setMatchInfo(serverResult) // serverResult是后台接口的返回值
  }, [matchId])

  return <div>{matchInfo}</div>;
}

然后,自定义Hook:

function useMatch(matchId){
  const [ matchInfo, setMatchInfo ] = React.useState('')
  React.useEffect(() => {
    // 请求后台接口获取赛事信息
    // ...
    setMatchInfo(serverResult) // serverResult是后台接口的返回值
  }, [matchId])
  return [matchInfo]
}

接下来,修改原来的Match组件

export default function Match({matchId}) {
  const [matchInfo]=useMatch(matchId)
  return <div>{matchInfo}</div>;
}

相比高阶组件,自定义Hook更加简单,也更加容易理解。

现在我们再来处理以下这种情况:

hocPage(
  hocMatch(
    hocDiv(DivComponent)
  )
)

我们的代码将不会出现这种不断嵌套情况,而是会变成下面这种:

export default function PageA({matchId}) {
  const [pageInfo]=usePage(pageId)
  const [matchInfo]=useMatch(matchId)
  const [divInfo]=useDiv(divId)

  return <ul>
    <li>{pageInfo}</li>
    <li>{matchInfo}</li>
    <li>{divInfo}</li>
  </ul>
}

是否需要改造旧的class组件?

现在我们了解到了Hook的好,所以就需要去改造旧的class组件。

官方推荐不需要专门为了hook去改造class组件,并且保证将继续更新class相关功能。

实际上我们也没有必要专门去改造旧项目中的class组件,因为工作量并不小。

但是我们完全可以在新的项目或者新的组件中去使用它。

总结

Hook是对函数式组件的一次增强,使得函数式组件可以做到class组件的state和生命周期。

Hook的语法更加简练易懂,消除了class的生命周期方法导致的重复逻辑代码,解决了高阶组件难以理解和使用困难的问题。

然而Hook并没有让函数式组件能做到class组件做不到的事情,它只是让很多事情变得更加简单而已。

class组件并不会消失,但hook化的函数式组件将是趋势。

原文地址:https://www.cnblogs.com/vvjiang/p/12101796.html

时间: 2024-08-01 05:23:00

关于为什么使用React新特性Hook的一些实践与浅见的相关文章

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入门系列之todolist代码优化(使用react 新特性,es6语法)

代码优化 1 /** 2 - 今天我们通过es6语法,以及react新特性来优化我们的todo-list 3 - 顺带解决上个版本的key报错问题 4 */ 使用es6的解构赋值优化代码 1 /** 2 - 当我们需要一个对象某个属性的时候,我们可以使用解构赋值,这样在后续的代码就不需要通过原对象不停调用属性去获取了 3 - const { index } = this.props -----> 这就是解构赋值,在后续同一作用域使用this.props.index的时候直接使用index就可以了

react新特性介绍

10月23日,React发布了16.6版本,在此版本中带来了一些非常有用的新特性.主要的新特性包括: context suspense memo React.memo() Using memo() React.memo() 是能作用在简单的函数组件,类似于React.PureComponent对于class组件的作用.它本质上是一个高阶函数,达到的效果就是,自动帮组件执行shouldComponentUpdate() , 但是只是执行浅比较 React.lazy() static context

JAVA8,JAVA9,JAVA10,JAVA11,Spring5新特性原理原理与实践

Java10它号称有109项新特性,包含12个JEP. 需要注意的是,本次Java10并不是Oracle的官方LTS版本,所以咱们可以先了解新特性.然后坐等java11的发布再考虑在生产中使用吧 特性列表 局部变量的类型推断 var关键字GC改进和内存管理 并行全垃圾回收器 G1垃圾回收器接口线程-局部变量管控合并 JDK 多个代码仓库到一个单独的储存库中新增API:ByteArrayOutputStream新增API:List.Map.Set新增API:java.util.Properties

React劲爆新特性Hooks 重构去哪儿网火车票PWA云资源

var link="www.marenzy.top"//完整视频资源获取地址,复制链接在浏览器打开 var vx="aishangit666"//如链接失效可添加微信 React劲爆新特性Hooks 重构去哪儿网火车票PWA 本课程先带你细数最近一年来React的新特性,如Hooks.Redux API,让你从头理解Hooks对传统React开发方式的革命性改变.再利用所学知识,在不同场景下,重构去哪儿网火车票系统,并使用PWA实现堪比原生APP体验的应用. Rea

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

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

[译文]React v16(新特性)

[译文]React v16(新特性) 查看原文内容 我们很高兴的宣布React v16.0发布了! 这个版本有很多长期被使用者期待的功能,包括: fragments (返回片段类型) error boundaries(处理错误) portals (挂载方式) custom DOM attributes (支持自定义DOM属性) improved server-side rendering (提升服务端渲染性能) reduced file size (减少文件大小) (下面逐一说明) render

OSChina 技术周刊第二十四期 —— C# 6.0 的新特性

每周技术抢先看,总有你想要的! 移动开发 [博客]Swift社交应用文本输入优化汇总 服务端开发/管理 [翻译]HTTP/1 的最佳实践并不适合 HTTP/2 [翻译]使用 AppDomain 存储实现大数据集合 [翻译]AngularJS 对比 React [软件]PHP 论坛软件 PHPHub [软件]监控和告警系统 Bosun [软件]多主机容器网络 SocketPlane [博客]Django学习笔记 [博客]Tornado源码分析-Web Framework [博客]话说对 Hiber

Entity Framework Core 2.0 新特性

本文翻译来自:https://docs.microsoft.com/en-us/ef/core/what-is-new/index 一.模型级查询过滤器(Model-level query filters) ef core2.0包含了一个新特性,我们叫他模型级查询过滤器(Model-level query filters).此特性允许使用Linq查询表达式直接定义在实体类型的元数据模型上.这样的过滤器会自动应用到任何LINQ查询所涉及的那些实体类型,包括间接引用的实体类型(对象引用,导航属性).