React函数组件Hooks的使用

Hooks

Hooks: 钩子 , 钓钩 , 钩住 ,是一些可以让你在函数组件里"钩入" React state 以及生命周期等特性的函数 ,是 React 16.8 的新增特性, 它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

作用:函数组件提供状态, 生命周期等原本在 Class组件中才提供的功能

  • Hooks只能在函数组件中使用
  • 可以理解为听过Hooks 为函数组件钩入 class 组件的特性

Hooks 前后 , 组件开发模式的对比

  • React v16.8 以前: class组件(提供状态) + 函数组件(展示内容)
  • React v16.8 及其以后:
    1. class组件(提供状态) + 函数组件(展示内容)
    2. Hooks(提供状态) + 函数组件(展示内容)
    3. 混用以上两种方式: 部分功能用 class 组件 , 部分功能用 Hooks + 函数组件

总结:

  1. 有了 Hooks 以后, 不能再把 函数组件称之为 无状态组件了, 因为 Hooks 可以为函数组织间提供了状态
  2. Hooks 是一些可以让你在 函数组件里"钩入"状态生命周期等特性

函数式组件的好处

  1. 函数本身比较简单 , 能够更好的胜任 根据状态来渲染UI这件事
  2. Hooks 让函数组件内部有了维护状态的能力
  3. Hooks带来了组件的 逻辑复用能力

策略

  1. react没有计划从React中移除class
  2. Hook 和现有代码可以同时工作,你可以渐进式地使用:
    1. 不推荐直接使用 Hooks 大规模重构现有组件
    2. 推荐新功能用 Hooks,复杂功能实现不了的,也可以继续用 class
    3. 找一个功能简单、非核心功能的组件开始使用 hooks
  3. class 组件相关的 API 在hooks中可以不用
    1. state与setState
    2. 钩子函数,componentDidMountcomponentDidUpdatecomponentWillUnmount
    3. ``this` 相关的用法
  4. 原来的内容还是要用的
    1. JSX:{}onClick={handleClick}、条件渲染、列表渲染、样式处理等
    2. 组件:函数组件、组件通讯
    3. React 开发理念:单向数据流状态提升

使用步骤

  1. 导入 useState函数
  2. 调用 useState函数 , 传入初始值 , 返回 状态修改状态的函数
  3. 使用
    1. 在 JSX 中展示 状态
    2. 特定的时机调用 修改状态的函数来改状态
import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [count, setCount] = useState(0)
  console.log(count)
  return (
        <div>
            {count}
            <button onClick={() => { setCount(count + 1) }}>点击+1</button>
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

  • 参数: 状态初始值, 比如传入 0 表示该状态的初始值为 0
    • 注意: 此处的状态可以是可以任意值, 而 class 组件中的 state必须是对象
  • 返回值: 数组 ,包含两个值 : 1.状态值(state) 2. 修改该状态的函数(setState)

使用数组解构简化 useState 的使用。约定:修改状态的函数名称以 set 开头,后面跟上状态的名称

总结

  1. Hooks是以use开头的函数
  2. useState的作用是 给函数组件提供 状态生命周期
  3. use的语法格式是:
    1. 入参是任意类型的数据,给函数组件设置一个初值
    2. 返回值是 状态值修改状态的方法

useState - 处理表单元素

步骤:

  1. useState初始化内容和修改内容的方法
  2. 向 input元素上设置 valueonChange属性
import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [inputVal, setinputVal] = useState('')
  console.log(inputVal)
  return (
        <div>
            <input value={inputVal} onChange={(e) => { setinputVal(e.target.value) }} />
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

useState可以轻松的完成受控组件的功能

useState - setXXX回调函数格式

useState 的两种格式

格式一: 传入值
useState(0) or useState('abc')

格式二: 传入回调
useState((=>{ return 初始值}))

  1. 回调函数的返回值就是作为状态的当前值
  2. 回调函数只能触发一次
    一般情况下我们都是直接传入值,但是你也可以传入一个回调函数,在回调函数中,对值进行一个处理后,作为当前的状态

使用场景

格式一: 传入值
如果状态就是一个普通的数据(比如, 字符串 , 数字 , 数组等) 都可以直接使用 useState(普通数据)

格式二: 传入回调

  1. 初始状态需要经过一些计算得到 , useState(()=>{这里可以进行计算 , return 结果})

setXXX的参数可以是回调

状态需要迭代累计的时候, setXXX((上一次的值)=>{return 新值})

示例

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [count, setCount] = useState(1)
  const hClick = () => {
    setCount(count + 1)
    setCount(count + 1)
    setCount(count + 1)
  }
  return (
        <div>
            count:{count}
            <button onClick={hClick}>多次连续setCount</button>
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

React函数组件Hooks的使用_第1张图片
我们可以看到,不是回调函数的情况下,它和 类组件中的 setState,都会有同样的情况,那就是 多次触发会 合并 ,我们认为的值 应该是 4, 可是输出的却是 2

如果想实现,count能够迭加的效果, 那我们就需要使用 回调函数为参数 , 在回调的形参中能够接收到上一次的值

代码改造

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [count, setCount] = useState(1)
  const hClick = () => {
    setCount((count) => count + 1)
    console.log(count)
    setCount((count) => count + 1)
    console.log(count)
    setCount((count) => count + 1)
    console.log(count)
  }
  return (
        <div>
            count:{count}
            <button onClick={hClick}>多次连续setCount</button>
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

React函数组件Hooks的使用_第2张图片
这次就能成功的实现迭加的效果,但是为什么会 console.log会输出的都是 1 呢 ? 这点我在类组件中讲到过, 还是因为 React的性能优化机制,导致的一个 异步操作

总结

  1. useState的参数有两种格式
    1. const [count, setCount] = useState(0)
    2. 回调函数 const [count,setCount] = useState(()=>{ return 初始值})
  2. setXXX的参数有两个格式 , 其中函数式可以用来对 state进行迭加
  setCount((上一次的值)=>{
    return 新值
  })

你真的学会了吗?
你真的学会了setXXX的参数为函数的使用场景了吗? 那我们来做个小小的题目吧

点击按钮, 让count 从60 开始倒计时

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [count, setCount] = useState(60)
  const hClick = () => {
    setInterval(() => {
      setCount((count) => count - 1)
    }, 1000)
  }
  return (
        <div>
            <h1>倒计时:{count}</h1>
            <button onClick={hClick}>点击开始倒计时</button>
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

注意事项
setXXX参数要使用函数形式才能实现迭加, 不然你会发现,点击开始倒计时触发定时器后,只会输出一次 99 就没有任何变化了,其实定时器还在走,并没有停止,而是因为, 你没有是用函数式, 那么每次一秒后,定时器都会执行 setCount,它拿到的永远不会是你上一次的值, 永远都是 100 的初始值 ,所以每次执行的都是 100 - 1

useState组件的更新过程

函数组件没有生命周期的概念 , 当然我这句话说的太绝对了 ,不是说没有 ,而是没有像 类组件中的生命周期钩子函数 , 但是在
在hooks的出现后 ,我们可以实现模仿类组件中的生命周期钩子函数

更新过程 - 示例代码

import React, { useState } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  const [count, setCount] = useState(0)
  return (
        <div>
            <h1>{count}</h1>
            // 点击 count + 1
            <button onClick={() => { setCount((count) => count + 1) }}>+1</button>
        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

函数组件使用 useState hook 后的执行过程 , 以及状态值的变化:

  • 组件第一次渲染:
    1. 执行该函数中的代码逻辑
    2. 调用 useState(0) 将传入的参数作为状态的初始值. 即 : 0
    3. 渲染组件 , 此时 , 获取到的状态 count 值为 : 0
      点击按钮 , 调用 setCount((count)=>{count+1}) 修改状态, 因为状态发生改变 ,所以该组件会重新渲染
  • 组件第二次渲染:
    1. 再次执行该组件中的代码逻辑
    2. 再次调用 useState(0):
      1. 此时 React内部会拿到最新的状态值而非初始值
      2. 该示例中的最新的状态值为 1
    3. 再选渲染组件 , 此时 , 获取到的状态 count值为: 1

useState 的初始值 (参数) 只会在组件第一次渲染时生效

const [count, setCount] = useState(()=>{
  console.log('useState...') // 这句只会输出一次
  return 0
})

也就是说 , 以后的每次渲染 , useState获取到都是最新的状态值 , React 组件会记住每次最新的状态值

总结

  • 状态更新,整个组件的逻辑重新运行一次;

  • useState只会在组件第一次渲染时使用状态的初值,随后都会使用状态的最新值

  • useState 这个 Hook 就是用来管理 state 的,它可以让函数组件具有维持状态的能力。也就是说,在一个函数组件的多次渲染之间,这个 state 是共享的。

useState最佳实践方案

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

  • 方案一 : useState({状态1, 状态2 ..... })
  • 方案二; useState(状态1) useState(状态2)

推荐使用方案二
调用 useState hook 多次即可 , 每调用一次 useState hook 可以提供一个状态

注意:
hooks : setXXX(新值) ===> 用新值去替换之前的值
class: setState({要修改的属性})

useState的使用规则

  1. useState 只能直接出现在 函数组件内部
  2. useState不能嵌套在 if / for
    原因: React 是按照 hooks 的调用顺序来识别每一个 hook , 如果每次调用的顺序不同 , 导致 React 无法知道是哪一个 hook , 可以通过开发者工具查看

useEffect - 理解副作用

事物的主要作用之外的 , 就是副作用 ,记住这句话,要考的!!! 划重点
函数式组件

  • 主作用: 就是根据数据 (state/ props) 渲染 UI
  • 副作用: 数据(Ajax)请求 , 手动修改 DOM , 开启定时器 , 清空定时器 , 添加事件监听 , 删除事件 , localStorage 等操作

记住一句话 , 除了渲染 UI 以外的操作, 都可以称之为是 副作用 , 可以把业务逻辑写在 useEffect()中 , 但不是所有的都必须

useEffect基本使用

使用步骤

import React, { useEffect } from 'react'
import ReactDOM from 'react-dom'

export default function App () {
  useEffect(() => {
    console.log('1 ---useEffect执行了.....')
  })

  useEffect(() => {
    console.log('2 ---useEffect执行了.....')
  })
  useEffect(() => {
    console.log('3 ---useEffect执行了.....')
  })
  return (
        <div>

        </div>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'))

执行时机

  • render完成之后 , 执行 Effect
  • 如果定义了多个 , 则顺序执行
    React函数组件Hooks的使用_第3张图片

useEffect 设置依赖项

在设置了依赖项后 , Effect只有在依赖项 发生变化时, 才执行相应的 回调

示例代码

import React, { useState, useEffect } from 'react'
import ReactDom from 'react-dom'
export default function App () {
  useEffect(() => {
    console.log('useEffect,更新title')
    document.title = 'count' + count
  })
  const [count, setCount] = useState(0)
  const [n, setN] = useState(1)
  return (
    <div>
      <h1>useEffect的依赖项</h1>
      <button
        onClick={() => {
          setCount(count + 1)
        }}>
        点击了{count}次,会更新到文档的标题上
      </button>
      <div>
        n:{n}
        <button onClick={() => setN(n + 1)}>点击,让n+1</button>
      </div>
    </div>
  )
}
ReactDom.render(<App />, document.getElementById('root'))

问题:

  1. 点击第第二button执行其中的代码,副作用会执行吗?
    会执行 , 因为并没有指定的依赖项 ,所以不管当哪个状态改变时, 整个函数组件都会重新执行渲染一次 ,那么只要重新渲染 ,那么 useEffect都会执行第一次
  2. 有必要执行么?
    没必要执行 , 因为我们只需要当 执行的值 ,例如 count发生变化 ,才执行 ,而不是 随便一个状态更新就导致 useEffect执行 , 做了多余的操作

useEffect的依赖项

useEffect有两个参数:
参数一: 副作用函数
参数二: 执行副作用函数的依赖项 : 它决定了什么时机执行 参数一(副作用函数)

useEffect的完整格式

情况一: 不带第二个参数 , 执行时机 : 每次更新之后都要执行
情况二: 带第二个参数 , 参数是空数组, 执行时机: 页面刷新 第一次才执行 , 只执行一次

useEffect(() => {
  // 副作用函数的内容
}, [依赖项])

使用场景:

  1. 事件绑定
  2. 发送请求获取数据等

因为这些操作 ,我们只需要执行一次 ,不需要它多次执行

情况三: 带第二个参数(数组格式) , 并指定了依赖项 , 执行时机: 1. 页面刷新加载初始执行一次 2. 依赖项的值发生了变化 ,执行一次

useEffect(() => {
  // 副作用函数的内容
}, [依赖项1,依赖项2,....])

你可能感兴趣的:(react.js,javascript,前端)