海安零距离 海安论坛 海安新闻 海安

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2001|回复: 0

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

[复制链接]

6234

主题

6234

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
18716
发表于 2019-12-27 14:09 | 显示全部楼层 |阅读模式
前言

关于Hook的界说官方文档是这么说的:
  1. Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的环境下利用 state 以及其他的 React 特性。
复制代码
简单来说,就是在利用函数式组件时能用上state,另有一些生命周期函数等其他的特性。
如果想相识Hook怎么用,官方文档和阮一峰的React Hooks 入门教程都讲得很清楚了,我发起直接看官方文档和阮大神的文章即可。
本篇博客只讲为什么要用React的Hook新特性,以及它办理了什么标题。
为什么利用Hook?

让我们先看看别人怎么说。
阮大神的文章中给了一个示例代码:
  1. 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 {buttonText};  }}
复制代码
而且提出:
  1. 这个组件类仅仅是一个按钮,但可以看到,它的代码已经很"重"了。真实的 React App 由多个类按照层级,一层层构成,复杂度成倍增长。再参加 Redux,就变得更复杂。
复制代码
实际上,上面这个代码的“重”有部门泉源于写法标题,他大概并没有“重”,让我们看看下面这种class写法:
  1. 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 {buttonText};  }}
复制代码
然后再对比下利用了Hook的函数式组件:
  1. import React, { useState } from "react";export default function Button() {  const [buttonText, setButtonText] = useState("Click me,   please");  function handleClick() {    return setButtonText("Thanks, been clicked!");  }  return {buttonText};}
复制代码
即使是我们简化过的class写法,比起Hook的看起来似乎也确实“重”了点。
Hook的语法确实轻便了一些,但是这个理由并不是那么充实。
阮大神同时摆列了Redux 的作者 Dan Abramov 总结了组件类的几个缺点:

  • 大型组件很难拆分和重构,也很难测试。
  • 业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。(这里我以为阮大神写的大概有点标题,应该是是各个生命周期方法更为准确)
  • 组件类引入了复杂的编程模式,比如 render props 和高阶组件。
这三点都是究竟,于是有了函数化的组件,但之前的函数化组件没有state和生命周期,有了Hook那么就可以办理这个痛点。
而且Hook并不但是这么简单,通过自界说Hook,我们可以将原有组件的逻辑提取出来实现复用。
用useEffect办理生命周期导致的重复逻辑或关联逻辑

上面举的几个缺点,第一点和第三点你大概很轻易明确,第二点就不轻易明确了,以是我们须要深入到详细的代码中去明确这句话。
我们看看下面这段代码:
  1. 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 {matchInfo}
  2. ;  }}
复制代码
如许的代码在我们的业务中经常会出现,通过修改传入赛事组件的ID,去改变这个赛事组件的信息。
在上面的代码中,受生命周期影响,我们须要在加载完毕和Id更新时都写上重复的逻辑和关联逻辑。
以是现在你应该比力好明确这句话:业务逻辑分散在组件的各个生命周期方法之中,导致重复逻辑或关联逻辑
为相识决这一点,React提供了useEffect这个钩子。
但是在讲这个之前,我们须要先相识到React带来的一个新的思想:同步。
我们在上面的代码中所做的实际上就是在把组件内的状态和组件外的状态举行同步。
以是在利用Hook之前,我们须要先摒弃生命周期的思想,而用同步的思想去思考这个标题。
现在再让我们看看改造后的代码:
  1. import React, { Component } from "react";export default function Match({matchId}) {  const [ matchInfo, setMatchInfo ] = React.useState('')  React.useEffect(() => {    // 哀求后台接口获取赛事信息    // ...    setMatchInfo(serverResult) // serverResult是后台接口的返回值  }, [matchId])    return {matchInfo}
  2. ;}
复制代码
看到这个代码,再对比上面的代码,你心中第一反应应该就是:简单。
React.useEffect担当两个参数,第一个参数是Effect函数,第二个参数是一个数组。
组件加载的时间,实行Effect函数。
组件更新会去判断数组中的各个值是否变动,如果稳定,那么不会实行Effect函数。
而如果不传第二个参数,那么无论加载还是更新,都会实行Effect函数。
趁便提一句,这里有组件加载和更新的生命周期的概念了,那么也应该是有组件卸载的概念的:
  1. import React, { Component } from "react";export default function Match({matchId}) {  const [ matchInfo, setMatchInfo ] = React.useState('')  React.useEffect(() => {    // 哀求后台接口获取赛事信息    // ...    setMatchInfo(serverResult) // serverResult是后台接口的返回值    return ()=>{      // 组件卸载后的实行代码    }  }, [matchId])    return {matchInfo}
  2. ;}}
复制代码
这个常用于事故绑定解绑之类的。
用自界说Hook办理高阶组件

React的高阶组件是用来提炼重复逻辑的组件工厂,简单一点来说就是个函数,输入参数为组件A,输出的是带有某逻辑的组件A+。
追念一下上面的Match组件,如果这个组件是页面A的首页头部用来展示赛事信息,然后现在页面B的侧边栏也须要展示赛事信息。
标题就在于页面A的这块UI须要用div,而页面B侧边栏的这块UI须要用到span。
包管本日早点放工的做法是复制A页面的代码到页面B,然后改下render的UI即可。
包管以后早点放工的做法是利用高阶组件,请看下面的代码:
  1. 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 (              )    }  }}const MatchDiv=hocMatch(DivUIComponent)const MatchSpan=hocMatch(SpanUIComponent)
复制代码
但是实际上有的时间我们的高阶组件大概会更复杂,比如react-redux的connect,这就是高阶组件的复杂化利用方式。
又比如:
  1. hocPage(  hocMatch(    hocDiv(DivComponent)  ))
复制代码
毫无疑问高阶组件能让我们复用很多逻辑,但是过于复杂的高阶组件会让之后的维护者望而却步。
而Hook的玩法是利用自界说Hook去提炼这些逻辑,首先看看我们之前利用了Hook的函数式组件:
  1. import React, { Component } from "react";export default function Match({matchId}) {  const [ matchInfo, setMatchInfo ] = React.useState('')  React.useEffect(() => {    // 哀求后台接口获取赛事信息    // ...    setMatchInfo(serverResult) // serverResult是后台接口的返回值  }, [matchId])    return {matchInfo}
  2. ;}
复制代码
然后,自界说Hook:
  1. function useMatch(matchId){  const [ matchInfo, setMatchInfo ] = React.useState('')  React.useEffect(() => {    // 哀求后台接口获取赛事信息    // ...    setMatchInfo(serverResult) // serverResult是后台接口的返回值  }, [matchId])  return [matchInfo]}
复制代码
接下来,修改原来的Match组件
  1. export default function Match({matchId}) {  const [matchInfo]=useMatch(matchId)  return {matchInfo}
  2. ;}
复制代码
相比高阶组件,自界说Hook更加简单,也更加轻易明确。
现在我们再来处置处罚以下这种环境:
  1. hocPage(  hocMatch(    hocDiv(DivComponent)  ))
复制代码
我们的代码将不会出现这种不停嵌套环境,而是会变成下面这种:
  1. export default function PageA({matchId}) {  const [pageInfo]=usePage(pageId)  const [matchInfo]=useMatch(matchId)  const [divInfo]=useDiv(divId)  return
  2. [list]   
  3. [*]{pageInfo}   
  4. [*]{matchInfo}   
  5. [*]{divInfo}  
  6. [/list]}
复制代码
是否须要改造旧的class组件?

现在我们相识到了Hook的好,以是就须要去改造旧的class组件。
官方保举不须要专门为了hook去改造class组件,而且包管将继续更新class干系功能。
实际上我们也没有须要专门去改造旧项目中的class组件,由于工作量并不小。
但是我们完全可以在新的项目大概新的组件中去利用它。
总结

Hook是对函数式组件的一次加强,使得函数式组件可以做到class组件的state和生命周期。
Hook的语法更加轻便易懂,消除了class的生命周期方法导致的重复逻辑代码,办理了高阶组件难以明确和利用困难的标题。
然而Hook并没有让函数式组件能做到class组件做不到的事变,它只是让很多事变变得更加简单而已。
class组件并不会消失,但hook化的函数式组件将是趋势。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|深圳论坛-深圳人的网上家园  

GMT+8, 2020-6-6 05:47 , Processed in 0.114805 second(s), 29 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表