• React16.8新增特性Hooks--概念理解


    1.Hooks概念理解

    1. 什么是hooks

    让你在不编写class的情况下使用state以及其他的React特性。

    Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”

    React体系里组件分为 类组件 和 函数组件

    函数组件是有利于逻辑拆分与重用的组件表达形式。而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks应运而生。

    注意点:

    1. 有了hooks之后,为了兼容老版本,class类组件并没有被移除,都可以使用
    2. 有了hooks之后,不能在把函数成为无状态组件了,因为hooks为函数组件提供了状态
    3. hooks只能在函数组件中使用

    2.Hooks解决了什么问题

    Hooks的出现解决了2个问题 1. 组件的状态逻辑复用 2.class组件自身的问题

    1. 组件的逻辑复用

      在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式,但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等

    2. class组件自身的问题

      class组件大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的组件。

    3. Hooks优势

    1. 告别难以理解的class
    2. 解决业务逻辑难以拆分的问题
    3. 使状态逻辑复用变得更加简单
    4. 函数组件在设计思想上,更加契合React的理念
    5. 一套能够使函数组件更强大,更灵活的“钩子”

    2.useState

    1.基础使用

    1.作用:

    ​ useState为函数组件提供状态(state)。

    2.使用步骤:

    1. 导入 useState 函数·
    2. 调用 useState 函数,并传入状态的初始值(必须在函数组件中
    3. useState函数的返回值中,拿到状态和修改状态的方法
    4. 在JSX中展示状态
    5. 调用修改状态的方法更新状态

    3.代码实现:

    返回值为:当前state以及更新state的函数 

    1. import { useState } from 'react'
    2. function App() {
    3. // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0
    4. // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)
    5. const [count, setCount] = useState(0)
    6. return (
    7. <button onClick={() => { setCount(count + 1) }}>{count}button>
    8. )
    9. }
    10. export default App

    点击按钮后,会看见state改变 

    总结:

    •  允许你在React函数组件中添加state的Hook。
    •  返回值为:包含当前state以及更新state的函数的数组。
    •  const [count, setCount] = useState(0)  
    •  count是state变量,接受0;用来初始化state
    •  首次渲染,将初始值给到count;后续只要调用了setCount,就会将最新值给到count
    •  setCount是改变state的函数,只要调用setCount一次,整个App中的代码都会执行
    • useState函数可以执行多次,每调用一次为函数组件提供一个状态
    •  注意:整个只能在函数组件中使用

    2. 状态的读取和修改

    1.读取状态

    ​ 该方式提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用

    2.修改状态

    1. setCount是一个函数,参数表示最新的状态值
    2. 调用该函数后,将使用新值替换旧值
    3. 修改状态后,由于状态发生变化,会引起视图变化

    3.代码实现:

    1.userState传过来的参数,作为count的初始值

    2.[count,setCount]:是一个解构赋值,useState返回值是一个数组

    3.名字可以自定义,保持语义化

    4.顺序不可以换,第一参数是数据状态,第二参数是修改数据的方法

    5.setCount函数:用来修改count,依然保持不能直接修改原值,还是生成一个新值替换原值

    setCount(基于原值计算得到的新值)

    6.count和setCount是一对:是绑在一起的setCount只能用于修改对应的count值

    1. import { useState } from 'react'
    2. function App() {
    3. const [count, setCount] = useState(0)
    4. return (
    5. <button onClick={() => { setCount(count + 1) }}>{count}button>
    6. )
    7. }
    8. export default App

    注意事项:

    ​ 修改状态的时候,一定要使用新的状态替换旧的状态,不能直接修改旧的状态,尤其是引用类型

    3.组件的更新过程

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

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

    注意:useState 的初始值(参数)只会在组件第一次渲染时生效。也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件会记住每次最新的状态值

    1. import { useState } from 'react'
    2. // 首次渲染:组件内部的代码会被执行一次
    3. // 更新渲染:setCount都会更新:
    4. // 1.app组件会再次渲染,这个函数会再次执行
    5. // 2.useState再次执行,得到的是新的count值,不是0,而是修改后的1
    6. // 模板会用新值渲染
    7. function App() {
    8. // count:数据状态
    9. // setCount:修改count的函数(专有函数)
    10. const [count, setCount] = useState(0)
    11. // 在这里可以进行打印测试
    12. console.log(count)
    13. return (
    14. <button onClick={() => { setCount(count + 1) }}>{count}button>
    15. )
    16. }
    17. export default App

    4. 使用规则

    1.useState函数可以执行多次

    每次执行互相独立,每调用一次为函数组件提供一个状态

    1. function List(){
    2. // 以字符串为初始值
    3. const [name, setName] = useState('cp')
    4. // 以数组为初始值
    5. const [list,setList] = useState([])
    6. }

    2.useState注意事项

    1.只能出现在函数组件中

    2.不能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook)

    1. //错误写法
    2. let num = 1
    3. function List(){
    4. num++
    5. if(num / 2 === 0){
    6. const [name, setName] = useState('cp')
    7. }
    8. const [list,setList] = useState([])
    9. }
    10. // 俩个hook的顺序不是固定的,这是不可以的!!!

    3.可以通过开发者工具component查看hooks状态

    4.只能在React的函数组件中调用Hook,不要在其他的js函数中调用(还有自定义Hook可以调用Hook)

    3.useEffect

    1. 理解函数副作用

    1.什么是副作用

    • ​副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。
    • 对于 React 组件来说,主作用就是根据数据(state/props)去渲染 UI,除此之外都是副作用(比如:数据获取,订阅或者手动修改 DOM)

    2.常见的副作用

    1. 数据请求 ajax发送
    2. 手动修改dom
    3. localstorage操作

    3.Effect Hook

    useEffect函数的作用就是为react函数组件提供副作用处理的。

    • useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。
    • 它跟 class 组件中的 componentDidMountcomponentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。

    2. 基础使用

    1.作用

    ​ 为react函数组件提供副作用处理

    2.使用步骤

    1. 导入 useEffect 函数
    2. 调用 useEffect 函数,并传入回调函数
    3. 在回调函数中编写副作用处理(dom操作)
    4. 修改数据状态(当我们通过修改状态更新组件时,副组件也会不断执行)
    5. 检测副作用是否生效

    3.代码实现

    在修改数据后,把count值放到页面标题中 

    1. import { useEffect, useState } from 'react'
    2. function App() {
    3. const [count, setCount] = useState(0)
    4. useEffect(()=>{
    5. // dom操作
    6. // 定义副作用
    7. document.title = `当前已点击了${count}次`
    8. document.title=count
    9. })
    10. return (
    11. <button onClick={() => { setCount(count + 1) }}>{count}button>
    12. )
    13. }
    14. export default App

    案例:

    定义state变量count,让页面的标题为count,并且随着count一起变化 

    使用类组件 

    1. class App extends React.Component {
    2. state = { count: 0 }
    3. changeCount = () => {
    4. this.setState({ count: this.state.count + 1 })
    5. }
    6. componentDidMount () {
    7. document.title = this.state.count
    8. }
    9. componentDidUpdate () {
    10. document.title = this.state.count
    11. }
    12. render () {
    13. return (
    14. <div>
    15. <h2>{this.state.count}h2>
    16. <button onClick={this.changeCount}>count+1button>
    17. div>
    18. )}}

    点击+1后,页面的值变了,但是页面标题没有改变。跟生命周期有关 ,因为生命周期只会执行一次,在页面渲染完毕后,componentDidMount会执行一次,然后就不会执行了,标题就一直等于0

     

     

     所以,在页面改变后,还要触发一个生命周期函数componentDidUpdate

    1. //当页面发生更新后,就会触发这个生命函数
    2. componentDidUpdate () {
    3. document.title = this.state.count
    4. }

    在class中,需要在两个生命周期函数中编写重复的代码, 会臃肿。

    使用函数组件  

    1. import { useEffect, useState } from 'react'
    2. function App () {
    3. const [count, setCount] = useState(0)
    4. //组件在首次渲染的时候执行一次
    5. //当组件修改状态更新组件的时候,副作用不断执行
    6. useEffect(() => {
    7. console.log(111)
    8. // 定义副作用
    9. document.title = count
    10. })
    11. return (
    12. <div>
    13. <h2>{count}h2>
    14. <button onClick={() => { setCount(count + 1) }}>{count}button>
    15. div>
    16. )}

    会执行两次,因为  ,会执行完一次后,在执行一次,用来检查组件的语法

    页面首次渲染执行一次,然后每次更新都会执行。 

    3.依赖项控制执行时机

    • useEffect--> 告诉 React 组件需要在渲染后执行某些操作。
    • React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。
    • Hook使用了js的闭包机制

    1. 不添加依赖项

    组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行

    1. 组件初始渲染
    2. 组件更新 (不管是哪个状态引起的更新)
    1. useEffect(()=>{
    2. console.log('副作用执行了')
    3. })
    • 不添加依赖项:首次渲染的时候执行一次,更新状态的时候不断执行。 

    2. 添加空数组

    组件只在首次渲染时执行一次。

    1. useEffect(()=>{
    2. console.log('副作用执行了')
    3. },[])
    • 第二个参数依赖项 [ ]:控制执行时机。
    • 依赖项为空数组,则只有首次页面渲染的时候执行一次。

    3. 添加特定依赖项

    副作用函数在首次渲染时执行,在依赖项发生变化时重新执行

    添加特定依赖项, 依赖项改变多少次,则执行多少次。
     

    1. function App() {
    2. const [count, setCount] = useState(0)
    3. const [name, setName] = useState('zs')
    4. useEffect(() => {
    5. console.log('副作用执行了')
    6. }, [count])
    7. return (
    8. <>
    9. <button onClick={() => { setCount(count + 1) }}>{count}button>
    10. <button onClick={() => { setName('cp') }}>{name}button>
    11. )
    12. }

    注意事项

    useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项就会有bug出现

    1. // 有count和name,所以都要添加到依赖项数组中
    2. useEffect(() => {
    3. // dom操作
    4. // 定义副作用
    5. document.title = `当前已点击了${count}次`
    6. document.title = count
    7. console.log(name)
    8. }, [count, name])
    9. // 此时:初始化+count/name被修改时都会执行副作用函数

    某种意义上,hook的出现,就是想不用生命周期概念也可以写业务代码

    总结:

    依赖项:控制副作用的执行时机
    1.默认状态(无依赖项)
    组件初始化时,先执行一次,等到每次数据修改组件更新再次执行(后续组件更新几次执行几次)
    2.添加一个空数组依赖项
    组件初始化的时候,执行一次,后续不再执行。
    3.依赖特定项
    组件初始化时,执行一次,依赖的特定项发送变化会再次执行 (后续依赖项改变几次执行几次)

    阶段小练习 - 自定义hook

    需求描述**:自定义一个hook函数,实现获取滚动距离Y

    const [y] = useWindowScroll()

    1.创建hooks/useWindowScroll.js,填入以下内容

    1. import { useState } from "react"
    2. export function useWindowScroll () {
    3. // geuy赋值,有固定的写法:setY
    4. const [y, setY] = useState(0)
    5. // 在滚动行为发生时,不断获取滚动值,然后交给y
    6. window.addEventListener('scroll', () => {
    7. const h = document.documentElement.scrollTop
    8. setY(h)
    9. })
    10. return [y]
    11. }

    2.在App.js中使用此函数

    1. import { useWindowScroll } from './hooks/useWindowSroll'
    2. function App () {
    3. const [y] = useWindowScroll()
    4. return (
    5. <div style={{ height: '12000px' }}>
    6. {y}
    7. div>
    8. )
    9. }
    10. export default App

    需求描述: 自定义hook函数,可以自动同步到本地LocalStorage

    const [message, setMessage] = useLocalStorage(key,defaultValue)

    1. message可以通过自定义传入默认初始值
    2. 每次修改message数据的时候 都会自动往本地同步一份

    1.创建useLocalStorage.js

    1. import { useEffect, useState } from 'react'
    2. export function useLocalStorage (key, defaultValue) {
    3. const [message, setMessage] = useState(defaultValue)
    4. // 每次只要message变化 就会自动同步到本地ls
    5. useEffect(() => {
    6. // 当数据变化时,往里面存入一份
    7. window.localStorage.setItem(key, message)
    8. }, [message, key])
    9. return [message, setMessage]
    10. }

    2.导入到App.js中使用

    1. import { useWindowScroll } from './hooks/useWindowSroll'
    2. import { useLocalStorage } from './hooks/useLocalStorage'
    3. function App () {
    4. const [y] = useWindowScroll()
    5. const [message, setMessage] = useLocalStorage('hook-key', 'zz')
    6. // 5秒后。从zz变成cp->Application的Key
    7. setTimeout(() => {
    8. setMessage('cp')
    9. }, 5000)
    10. return (
    11. <div style={{ height: '12000px' }}>
    12. {y}
    13. {message}
    14. div>
    15. )
    16. }
    17. export default App
  • 相关阅读:
    leetcode(力扣) 46. 全排列(回溯)
    【编程语言】Python平台化为何比Java差?
    打开网站出现Internal Server Error的原因和解决方法
    在Linux系统启动java程序(jar包)
    JAVA开发中的Maven搭建以及相关操作
    VideoMAE 论文阅读
    前端使用highlight.js代码高亮显示(服务端返回前端代码的字符串格式)
    UNDO表空间使用率过高的处理方式
    centos7的yum修改为阿里源
    NGINX重写功能和防盗链
  • 原文地址:https://blog.csdn.net/m0_54088431/article/details/126808534