乐趣区

关于react.js:扔掉你的Class吧Hooks太香了一

简介

Hook 是 React16.8 中新增的个性,它能够让你不编写 class 组件的状况下应用诸如 state,生命周期等个性。

动机

在组件之间复用状态逻辑很难

为了解决这类问题,也有呈现过一些解决方案,如 render props 高阶组件 Context 等,它们都会导致组件树造成 嵌套天堂(有点像回调函数和 flutter 的写法)。

有了 Hook 之后,就能够从组件中提取状态逻辑,使得这些逻辑能够独自测试并复用。Hook 使你在无需批改组件构造的状况下复用状态逻辑

简单组件变得难以了解

随着业务扩张,组件内的逻辑状态和副作用也越来越多。咱们在写 class 组件时,通常会将数据的获取放到 compoentDidMountcomponentDidUpdate中,波及到定时器或者数据订阅时,还须要在生命周期函数 compoentWillUnmount 去革除定时器或者勾销数据订阅。这使得齐全不相干的代码组合到一起,很容易产生 bug,并且导致逻辑不清晰。

为了解决这个问题,Hook 将组件中互相关联的局部拆分成更小的函数(比方设置订阅或申请数据),而并非强制依照生命周期划分

难以了解的 class

在之前,如果你要应用诸如 state 生命周期 等个性,就必须引入 class 组件,如果你要应用这些就必须花工夫去了解 JavaScript 中 this 的工作形式。

为了解决这些问题,Hook 使你在非 class 的状况下能够应用更多的 React 个性

概览

Hook 能够分为 内置 Hook 自定义 Hook

内置 Hook 有:

  • useState
  • useEffect
  • useContext
  • useReducer
  • useCallback
  • useMome
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

自定义 Hook就是将上述 内置 Hook进行组合,导出为一个以 use 结尾的函数。


这里先写一个基于 class 的 Counter 组件,前面让咱们来用 Hook 将它进行革新。

`import React from ‘react’
class Counter extends React.Component {
  constructor(props) {
    super(props)
    this.state = {count: 0}
    this.setCount = this.setCount.bind(this)
  }
  setCount() {
    this.setState({
      count: this.state.count + 1
    })
  }
  
  render() {
    return (
      <>
        <h2>{this.state.count}</h2>
        <button onClick={this.setCount}>+</button>
      </>
    )
  }
}
`

useState

让咱们先来看一个最根底也同样重要的 Hook,当初咱们开始革新下面的 class 组件。

  • 首先将 class 组件转为 function 组件,也就是 函数式组件

`- class Counter extends React.Component {}
+ function Counter() {}
`

  • 接下来让咱们将 state 等相干代码删掉,改用useStateHook,因为此处改变较多,间接上最终代码

`import React, {useState} from ‘react’
const Counter = () => {
  // 数组解构语法
  // 申明一个 count 变量,并申明一个 setCount 函数用于扭转 count 的值
  const [count, setCount] = useState(0)
  
  return (
    <>
      <h2>{count}</h2>
      <button onClick={() => setCount(count + 1) }>+</button>
    </>
  )
}
`

乍一看代码简洁了很多,这难道不正是咱们想要的嘛。(Hook 真香)

如果咱们 有多个 state 变量,能够写成上面这种模式

`const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const [name, setName] = useState(”)

`

如果本次更新依赖于 上一次的 state 值 ,能够这么写,此时setCount 接管一个函数作为参数

`const [count, setCount] = useState(0)
const handleClick = () => {
  setCount(prev => prev + 1)
}
<button onClick={handleClick}>+</button>
`

useEffect

useEffect 能够让你在 函数式组件 执行一些副作用操作,它接管两个参数,第一个参数为必选的副作用函数,第二个为可选的参数(它是副作用函数执行的依赖项数组)

还是方才的例子,如果咱们心愿组件更新后执行一些副作用,比方打印以后 count 变量的值,又或者扭转以后页面的 title

“// …
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
useEffect(() => {
  console.log(以后 count 值,${count})
  document.title = 以后 count 值,${count}
})
// …

下面的代码咱们能够看到咱们并没有增加第二个可选参数,这意味着,只有组件内状态产生扭转(不仅仅是 count,甚至 num 产生扭转时),这个副作用函数都会执行。如果咱们心愿副作用函数 只在 count 扭转 时候执行,这时候咱们的第二个参数就派上用场了()

“// …
// 以后 effect 函数只依赖于 count 的扭转,在 num 扭转的时候并不会被执行
// 相当于 class 组件中的 compoentDidMount 和 componentDidUpdate
useEffect(() => {
  console.log( 以后 count 值,${count})
  document.title = 以后 count 值,${count}
}, [count])
// …

如果咱们仅仅心愿副作用函数在组件挂载时候执行(compoentDidMount

“// useEffect 约定,在第二个参数传递空数组时,以后副作用函数只在组件挂载时被执行
useEffect(() => {
  console.log( 以后 count 值,${count})
  document.title = 以后 count 值,${count}
}, [])

有些时候咱们可能会用到定时器,比方每秒钟更新一次工夫

`const [time, setTime] = useState(new Date())
const timer = null
useEffect(() => {
  timer = setInterval(() => {
    setTime(new Date())
  }, 1000)
})
`

当初的代码有一个问题,想必大家都晓得,那就是timer 没有被革除,咱们在工作中会遇到很多这种场景,那么咱们应该怎么去革除 timer 呢,答案是useEffect 的第一个参数返回一个函数,这个被返回的函数会在组件被卸载时候主动被执行,能够革除诸如定时器之类的货色

`const [time, setTime] = useState(new Date())
const timer = null
useEffect(() => {
  timer = setInterval(() => {
    setTime(new Date())
  }, 1000)
  
  // 相似于 compoentWillUnMount
  
  // 当然这里也能够是一个具名函数
  return () => {
    clearInterval(timer)
    timer = null
  }
})
`

退出移动版