:::info Hooks介绍:
Hooks是React 16.8版本新增的特性,它可以让我们在不编写class组件的情况下使用组件的状态state以及其他的react特性(比如生命周期),类组件能实现的功能我们都可以直接或者间接通过hooks来实现。 :::

1、为什么需要Hooks?

:::info

  • class类组件的优点:组件可以定义、声明自己的state,用来维护和保存组件自身身的状态。
  • 函数式的组件不可以,函数组件的每次调用(也就是组件的渲染)都会产生新的临时变量,在组件的内部产生,组件销毁对应的临时变量也会销毁。
  • class类组件有自己的生命周期钩子,并在自己的生命周期中完成相应的逻辑任务,而函数式组件没有自己的生命周期方法。无法像类组件那样在某些时刻完成一些逻辑代码的执行。
    • 比如在componentDidMount中发送网络请求,并且该生命周期函数只会执行一次。
    • 函数式组件在学习hooks之前,如果在函数中发送网络请求,意味着每次重新渲染都会重新发送一次网络请求。
  • class组件可以在状态改变时只会重新执行render函数以及我们希望重新调用的生命周期函数componentDidUpdate等。
  • 函数式组件在重新渲染时,整个函数都会被执行,似乎没有什么地方可以只让它们调用一次。 :::

2、class类组件存在的问题?

:::info 在以往的开发中,类组件是存在一些列的问题,而总的来说,hook的出现,让我们可以彻底的不再使用类组件来编写相应的程序。全面转向使用函数组件,让我们以一种函数式编程思想来完成项目的迭代、开发。总的来说,hooks的出现,改变了编程习惯,hooks编程的颗粒度更加细化,方便和简洁。 :::

2.1 复杂组件变得难以理解

:::info

  • 我们在最初编写一个class组件时,往往逻辑比较简单,并不会非常复杂。但是随着业务的增多,我们的class组件会变得越来越复杂、越来越臃肿。
  • 比如componentDidMount中,可能就会包含大量的逻辑代码:包括网络请求、一些事件的监听(还需要在componentWillUnmount中移除)。
  • 事实上、对于这样的class组件实际上非常难以拆分,因为它们的逻辑往往混在一起,强行拆分反而会造成过度设计,增加代码的复杂度。 :::

2.2 难以理解的class

:::info

  • 学习ES6的class是学习react的一个障碍。
  • 比如在class中,我们必须搞清楚this的指向到底是谁,而且书写起来还是一件比较麻烦的使用。所以需要花很多的精力去学习this和理解this的高级技巧。
  • this的处理起来依然非常麻烦。 :::

2.3 组件里面的状态难以复用

:::info

  • 在前面为了一些状态的复用我们需要通过高阶组件或render props
  • 像我们之前学习的redux中connect或者react-router中的withRouter,这些高阶组件设计的目的就是为了状态的复用
  • 或者类似于Provider、Consumer来共享一些状态,但是多次使用Consumer时,我们的代码就会存在很多嵌套。在开发与调试的过程中,代码变得臃肿、嵌套层次深,给调试程序与后期的维护带来了一定的困难。 :::

3、Hook组件的出现

:::info hook组件的出现,可以有效的解决上述出现的问题。hooks的优点:

  • 它可以让我们在不编写class的情况下使用state以及其他的React特性。
  • 但是我们可以由此延伸出非常多的用法,来让我们前面所提到的问题得到解决。

hooks的使用场景:

  • Hook的出现基本可以代替我们之前所有使用class组件的地方(除了一些非常不常用的场景)。
  • 但是如果是一个旧的项目,你并不需要直接将所有的代码重构为Hooks,因为它完全向下兼容,你可以渐进式的来使用它。

Hook只能在函数组件中使用,不能在类组件,或者函数组件之外的地方使用。 :::

4、useState函数Hook的使用

:::info useState原理:useState的本质上是一个函数,我们将一个普通的数据作为参数传入useState函数中,该函数给我们返回这个原始的状态数据和修改这个状态数据的方法,一旦我们的数据发生了改变,那么我们的页面将会自动进行渲染。 :::

4.1 useState的具体使用方法

  1. // useState hook的使用方法
  2. import { useState } from 'react';
  3. export default function Counter() {
  4. // 定义最初组件初始的状态 该hook的返回值是数组
  5. // 数组的第一个元素是原始的数据,第二个参数是修改状态的方法
  6. const [counter, setCounter] = useState(100)
  7. // 定义操作数据的行为
  8. function decrement() {
  9. setCounter(counter - 10)
  10. }
  11. return (
  12. <div>
  13. // 在页面中可直接进行使用
  14. <h2>{ counter }</h2>
  15. // 修改数据
  16. <button onClick={ () => setCounter(counter + 10) }>增加+10</button>
  17. <button onClick={ () => decrement() }>减少-10</button>
  18. </div>
  19. )
  20. }

4.2 useState使用的小细节

:::info

  1. useState可以多次使用,我们组件本身有多少个状态需要进行管理,那么我们就可以多次使用的useState的 hook。这样,可以让我们的状态颗粒度更细。
  2. 在修改状态的时候,我们必须传入一个新的状态,因为我们传入的新状态的值,在react的内部会进行比较,如果传入的是同一个值,那么react将不会重新渲染这个组件,所以在我们操作复杂类型的数据的时候,需要先将原始的数据进行拷贝,然后对拷贝的数据进行修改,最后将修改后的数据传给修改状态的函数。那么我们组件的状态就发生了变化,组件就会重新进行渲染。我们的页面就会展示最新的状态了。
  3. 记住,引用类型数据进行修改的时候,一定要先对其进行拷贝,后修改状态,这样我们的组件才会重新进行渲染。 ::: ```javascript import React, { useState } from ‘react’

export default function ComplexStateHook() { // 使用hook管理多个状态 我们的useState是可以使用多次的 const [counter, setCounter] = React.useState(100) const [age, setAge] = React.useState(18) const [todos, setTodos] = useState([‘吃饭’,’睡觉’, ‘打豆豆’])

// 复杂数据类型 const [friends, setFriends] = useState([ { id: ‘110’, name: ‘tom’, age: 13 }, { id: ‘111’, name: ‘jack’, age: 18 }, { id: ‘112’, name: ‘rose’, age: 20 } ])

// 添加要做的事情 正确的修改状态的方式 function addTodos() { const newTodos = […todos]; newTodos.push(“打代码”) setTodos(newTodos) }

// 添加要做的事情 错误的做法 function addTodo() { todos.push(“打游戏”) setTodos(todos) }

// 添加朋友 function addAge(index) { // 在这里需要对传入的数据进行拷贝 每次修改状态的时候应该传入一个新的状态 // 在react的内部会进行判断,如果传入的是同一个对象 那么页面将不会自动进行更新 // 如果传入的新状态不是同一个对象 页面将会自动更新 const newFriend = […friends]; newFriend[index].age += 1; // 修改状态 setFriends(newFriend) }

return (

当前数字: { counter }

年龄: { age }

要做的事情

    { todos.map((item, index) => { return
  • {item}
  • }) }

好友列表

    { friends.map((item, index) => { return
  • 姓名:{ item.name } 年龄:{ item.age }
  • }) }
) }

  1. <a name="nWJ5g"></a>
  2. ## 4.3 使用过程中需要的注意事项
  3. > useState函数的参数可以传入一个箭头函数作为参数,箭头函数的返回值就是我们要管理的状态。
  4. ```javascript
  5. // 可以使用箭头函数作为参数
  6. const [counter, setCounter] = useState(() => 100)
  7. conuter就是组件最新的状态
  8. setCounter就是更新状态的方法 我们只需要将新的状态作为参数传递到这个更新状态的函数 react的内部就会主动调用我们组件render方法,更新界面

:::info setCounter函数中可传入一个箭头函数 箭头函数的第一个参数是上一次修改的新的状态值。
我们也可以直接将最新的状态传入,组件也会进行更新 :::

  1. // setCounter()修改状态 可传入一个箭头函数作为参数 箭头函数的参数 就是最新的状态的值
  2. <button onClick={ () => setCounter( previous => previous + 1 ) }>修改状态</button>