【深入理解React】04_理解React Hook

前言

在提及到react hook的时候,一般是跟函数组件结合在一起的,即react hook只存在于函数组件中,在类组件中不受用,这是为什么?react hook的出现是解决了什么?

函数组件和类组件

在聊这些之前,先来认识认识一下函数组件和类组件。类组件是有状态组件,而函数组件是无状态组件

// 类组件
class App extend React.component{
    constructor(props){
        super(props);
        this.state = {
            text: '我是一个组件'
        }
    }
    componentDidMount(){}
    render(){
        return (
        	<div>我是一个类组件</div>
        )
    }
}

export defalut App;
// 函数组件
function App(props){
    return (
    	<div>我是一个函数组件</div>
    )
}

由上面我们不难发现,两者的区别:

  • 类组件需要继承class,函数组件不需要;
  • 类组件可以访问生命周期方法,函数组件不行;
  • 类组件中可以获取实例化化后的this,并基于这个this做各种事情;
  • 类组件可以定义并维护state,而函数组件不行。

React框架的设计理念是UI = render(data),显然函数组件更加契合这个理念。虽然类组件继承了React.Component,比函数组件多了很多功能,如state和生命周期,但是大而全”的背后,是不可忽视的学习成本,而函数组件轻量、灵活,学习成本也很低。就拿上面的栗子来说,其实只是想在页面上显示一行文字,类组件的代码量明显高于函数组件的代码量。

React Hook的设计初衷

虽然函数组件更符合React的设计理念,但是由于无状态、无生命周期,导致了它在某些地方并不实用,于是React团队开始引进了React Hook,补齐了这方面的“缺失”。

接下来介绍几个常用的Hook方法:

useState

const [text, setText] = useState('初始化文本');

useState的出现,便于了函数组件也可以使用**状态(state)**了。

useEffect

useEffect的出现,可以解决生命周期的问题。过去我们习惯放在componentWillMount、componentDidUpdate和componentWillUnmount三个生命周期里来做的事情可以放在useEffect中来做。

// 每次渲染都会被执行
useEffect(() => {})

// 仅在挂载阶段执行一次
useEffect(() => {}, [])

// 在useEffect中返回一个函数,该函数会在组件被卸载的时候执行
// 卸载函数的执行并不会受第二个参数或者其他因素的影响
// 仅在挂载和卸载的时候执行
useEffect(() => {
    return () => {}
},[])

// 当[]中的数据发生改变时,就会被执行
useEffect(() => {}, [])

更多react hook可以翻阅官方文档,或者我之前写的 React hook

React Hook让我们在函数组件的开发中更加方便了,没有了难以理解的class,不用去思考如何解决this问题,还解决了监听在componentDidMount、取消监听在componentWillUnmount这种业务逻辑难以拆分的问题。

Reat Hook的局限性

凡是有利就有弊,官方也没有说React Hook的出现可以让函数组件完全替代类组件,原因有几个:

  • 现有的hook api并不能完全补齐类组件的能力,比如getSnapshotBeforeUpdate
  • 由于函数组件的轻量性,将不能很好地将复杂组件拆分出来;
  • React Hook在使用层面还有严格地规则约束,只能在函数组件中使用,且不能在循环、条件和嵌套函数中使用。

React Hook的工作原理

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useStateuseEffect 调用之间保持 hook 状态的正确。

举个栗子:

import { useState } from 'react'
let isMounted = false;

function App(){
  let name, setName, age, setAge: (arg0: number) => void
  
  console.log('isMounted', isMounted);

  if(!isMounted){
    [name, setName] = useState('sugarMei');
    isMounted = true;
  }

  [age, setAge] = useState(99);

  const handleClick = () => {
    setAge(100);
  }

  return (
    <>
      <p>姓名:{name}</p>
      <p>年龄:{age}</p>
      <button onClick={handleClick}>点击修改年龄</button>
    </>
  )
}

export default App

这时候就会报错了。React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render

React Hook源码中,在组件首次挂载时,由于isMount = false, 会将useState生成两个hook对象,并按照顺序形成一个hook链,即链表;当页面更新时,由于isMount = false,所以跳过了setName,即相当于源码中遍历链表,结果直接跳过了某个节点,那我还如何寻找下一个节点?所以就报错了。

所以关于react hook需要知道以下几点:

  • react hook使用的是链表结构;
  • hook的顺序决定了自身在链表中的位置;
  • 组件每次渲染的时候,会随着链表读取hook对象。

总结

  • 类组件比函数组件拥有更多的功能,如state、声明周期等,但是由于功能越多,学习的成本越多,也容易被误操作;
  • 函数组件轻而小,符合React的设计理念,结合Raect hook可以解决state和生命周期等问题;
  • 函数组件还无法完全取代类组件;
  • react hook只能在函数组件中使用,且无法在循环,条件或嵌套函数中调用。

参考


如有错误,欢迎指出,感谢~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值