React学习 day-05

day-05

Hooks

Hooks:钩子、钩住,是 React@16.8 提供的新功能。

Hooks作用: 能为函数式组件提供状态和生命周期,Hooks 只能在函数组件中使用

Hooks(提供状态和生命周期)+函数组件(展示内容)

Hooks解决了什么问题

  • 组件的状态逻辑复用问题

    a,在 Hooks 之前,组件的状态逻辑复用经历了:mixins(混入)、HOC(高阶组件)、render props 等模式。

    b,(早已废弃)mixins 的问题:数据来源不清晰;命名冲突。

    c,HOC、render props 的问题:重构组件结构,导致组件形成 JSX 嵌套地狱问题。

  • class组件自身的问题

    a,选择:函数组件和 class 组件之间的区别以及使用哪种组件更合适。

    b,需要理解 class 中的 this 是如何工作的。

    c,同一业务的状态和业务逻辑被拆分到不同位置。

  • 相比于函数组件来说,类组件不利于代码压缩和优化,也不利于TS的类型推导

    # 例如不能把 componentDidMount 压缩成 c
    # 例如写 this 的时候没有提示,因为 this 只有在调用的时候才能确定指向,编写代码期间 TS 是不知道的
    

Hooks 渐进策略

目标

能够了解 Hooks 和之前 class 的写法是可以共存的。

内容

  • 官方没有计划从 React 中移除 class 组件。

  • Hooks 和现有代码可以同时工作,建议渐进式地使用它们。

    a,不推荐:大规模使用 Hooks 直接重构现有组件。

    b,推荐:新功能用 Hooks,Hooks 实现不了的复杂功能,也可以继续用 class。

    c,具体操作,从一些功能简单、非核心功能的组件开始使用 Hooks。

  • 不能在 Hooks 组件中,使用 class 组件相关的 API。

    a,state 与 setState。

    b,钩子函数,componentDidMountcomponentDidUpdatecomponentWillUnmount

    c,this 相关的用法。

  • 原来学习的绝大部分知识点还是要用的。

    a,JSX:{}onClick={handleClick}、条件渲染、列表渲染、样式处理等。

    b,组件:函数组件、组件通讯。

    c,React 开发理念:单向数据流状态提升 等。

    d,解决问题的思路、技巧、常见错误的分析等。

useState 使用

作用:

为函数组件提供状态和修改状态的方法。

使用:

  1. 导入 useState 函数。
  2. 调用 useState 函数,并传入状态的初始值。
  3. useState 函数的返回值中,拿到状态和修改状态的函数。
import React, { useState } from 'react'

export default function App() {
    //参数:代表默认值
    //返回值:数组[初始数据,修改数据的方法]
  const [count, setCount] = useState(0)
  return (
    <div>
      <h2>计数器:{count}</h2>
      <button
        onClick={() => {
          setCount(count + 1)
        }}
      >
        +1
      </button>
    </div>
  )
}

细节

  • 参数:初始状态,比如传入 0 就表示该状态的初始值为 0。
  • 注意:此处的状态可以是任意值(比如,数值、字符串、对象等),注意 class 组件中的 state 必须是对象。
  • 返回值:数组,数组里面包含两个值,状态和修改该状态的方法。
  • 约定:修改状态的方法以 set 开头,后面跟上状态的名称。

注意事项

  • 读取状态

    目前,useState 只能在函数组件内部调用(也可以在后续学习的自定义 Hook 中使用),所以返回的状态也是函数内部的局部变量。

  • 修改状态

    a,setCount(newValue) 是一个函数调用,参数表示新的状态值。

    b,调用该函数后,将使用新的状态值直接替换旧状态。

    c,修改状态后,组件会自动重新渲染。

  • 状态的不可变性(修改状态的时候,要使用新的状态替换掉旧的状态,而不要直接修改原状态)。

import React, { useState } from 'react'

const App = () => {
    const [obj, setObj] = useState({
        count: 0,
    })
    const handleClick = () => {
        // Error
        obj.count++
        setObj(obj)
        // Right
        /* setObj({
            count: obj.count + 1,
        }) */
    }
    return (
        <div>
            <p>{obj.count}</p>
            <button onClick={handleClick}>click</button>
        </div>
    )
}

export default App

useState 与组件更新过程

目标

能够说出使用功能 useState() 之后,组件的更新过程。

内容

  • 组件第 1 次渲染
    1. 调用函数式组件,从头开始执行组件中的代码逻辑。
    2. 调用 useState(0) 将传入的参数作为初始状态值,即:0。
    3. 开始渲染组件,此时得到的状态 count 值为:0。
  • 组件第 2 次渲染
    1. 点击按钮,调用 setCount(count + 1) 来修改状态,因为状态发生改变,所以,该组件会重新渲染。
    2. 组件重新渲染时,会再次执行该组件中的代码逻辑。
    3. 再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如该案例中的最新状态值为 1。
    4. 再次渲染组件,此时,获取到的状态 count 值为:1。
  • 注意:useState 的初始值(参数)只会在组件第一次渲染时生效,也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件内部会记住每次更新后的最新状态值!

useState 另一种写法

目标

掌握 useState(回调函数) 的写法。

内容

  • useState(回调函数),回调函数的返回值就是状态的初始值,该回调函数只会触发一次。
useState(() => {
    return 初始值
})
  • 该使用哪种形式?

    a,如果状态就是需要一个普通数据(没有逻辑、无需计算),那么推荐 useState(普通的数据)

    b,如果状态是经过一些计算得到的,此时,推荐使用 useState(回调函数)。因为改回调函数只会执行一次,节省性能。

import React, { useState } from 'react'

export default function App() {
  // let defaultCount = 0
  // console.log(1)
  // for (let i = 1; i < 10000; i++) {
  //   defaultCount += i
  // }
  // const [count, setCount] = useState(defaultCount)
  console.log(1)
  const [count, setCount] = useState(() => {
    let defaultCount = 0
    console.log(2)
    for (let i = 1; i < 10000; i++) {
      defaultCount += i
    }
    return defaultCount
  })
  return (
    <div>
      <h2>计数器:{count}</h2>
      <button
        onClick={() => {
          setCount(count + 1)
        }}
      >
        +1
      </button>
    </div>
  )
}

useState 使用细则

目标

掌握如何为函数组件提供多个状态以及注意点。

内容

  • 如何为函数组件提供多个状态?

    多次调用 useState 即可,每一次调用返回的 [state, setState] 之间,互不影响。

  • useState 的使用细则。

    a,目前,只能直接出现在函数组件中(其他 Hook 也一样)。

    b,不能嵌套在 if/for/其他函数 中!(if 的条件判断、for 循环的次数、函数的调用与否都可能会影响 hook 的顺序)。

    c,React 是按照 Hooks 的调用顺序来识别每一个 Hook,如果每次调用的顺序不同,导致 React 无法知道是哪一个状态和修改状态的方法。

    d,可以通过开发者工具进行查看 React 对 Hook 的管理。

import React, { useState } from 'react'

export default function App() {
    const [num1, setNum1] = useState(0)
    const [num2, setNum2] = useState(3)
    const [num3, setNum3] = useState(6)
    return (
        <div>
            <div>
                num1: {num1}
                <button onClick={() => setNum1(num1 + 1)}>修改 num1</button>
            </div>
            <div>
                num2: {num2}
                <button onClick={() => setNum2(num2 + 1)}>修改 num1</button>
            </div>
            <div>
                num3: {num3}
                <button onClick={() => setNum3(num3 + 1)}>修改 num1</button>
            </div>
        </div>
    )
}

useEffect 副作用介绍

目标

能够说出什么是副作用(side effect)。

内容

  • 类比,对于 999 感冒灵来说。

    a,主作用:用于治疗感冒引起的头痛,发热,鼻塞,流涕,咽痛等。

    b,副作用:可见困倦、嗜睡、口渴、虚弱感。

  • 那组件或一般函数的副作用是什么呢?

    a,组件的副作用:对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用,比如手动修改 DOM、数据(AJAX)请求、localStorage 操作等。

    b,函数的副作用:如果一个函数修改了其局部环境之外的数据,那么它就被称为有副作用。

  • 关于 useEffect。

    作用:当你想要在函数组件中处理副作用(side effect),就要使用 useEffect 了。

总结

  • 对于 React 组件来说,“主作用”是什么?
  • 常见的有哪些“副作用”?

useEffect 基本使用

目标

能够在函数组件中操作 DOM(处理副作用)。

内容

  • 执行时机:初始化时和数据变化的时候执行。
  • 相当于 class 中的 componentDidMount + componentDidUpdate。

需求

在实际开发中,副作用是不可避免的。

上一篇:React Hooks 特性


下一篇:react Hooks 封装 useState