当前位置: 首页 > article >正文

REACT学习第三幕--沉睡花园

什么是Hooks?

在react中,useState以及任何其他以use开头的函数都称为Hook(钩子),所以Hooks就是代表着use函数的集合,也就是钩子的集合

Hooks就是一堆功能函数,一个组件想要实现哪些功能就可以引入对应的钩子函数,像插件一样非常方便

Hooks分为:内置Hooks,自定义Hooks,第三方Hooks

用ref引用一个值做记忆功能

普通变量在状态更新的时候不会保留

import { useState } from "react"

function App(){
    const [count,setCount] = useState(0)
    let num = 0
    const handleClick=()=>{
        setCount(count+1)
        num++
        console.log(num)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>计数</button>
            <br/>
            {count}
        </div>
    )
}

export default App

这样点击之后会发现num没变: 

 

 对于普通变量来说ref有记忆的功能

useRef返回的是一个对象,这样改完以后状态更新也能保留了:

import { useState,useRef } from "react"

function App(){
    const [count,setCount] = useState(0)
    let num = useRef(0)
    const handleClick=()=>{
        setCount(count+1)
        num.current++
        console.log(num.current)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>计数</button>
            <br/>
            {count}
        </div>
    )
}

export default App

看一下Ref和State的区别:

  

来看个计时器例子:

import { useState,useRef } from "react"

function App(){
    const [count,setCount] = useState(0)
    let timer = null
    const handleClick=()=>{
        setCount(count+1)
        clearInterval(timer)    //根本清不完计时器,因为这个是另一个作用域下的
        timer = setInterval(()=>{
            console.log(123)
        },1000)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>计数</button>
            <br/>
            {count}
        </div>
    )
}

export default App

这样的话就相当于出bug了

但是用useRef就会解决这个问题:

import { useState,useRef } from "react"

function App(){
    const [count,setCount] = useState(0)
    const timer = useRef(null)
    const handleClick=()=>{
        setCount(count+1)
        clearInterval(timer.current)    //根本清不完计时器,因为这个是另一个作用域下的
        timer.current = setInterval(()=>{
            console.log(123)
        },1000)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>计数</button>
            <br/>
            {count}
        </div>
    )
}

export default App

useRef通过ref操作DOM

由于React会自动处理更新DOM以匹配你的渲染输出,因此在组件中通常不需要操作DOM,但是,有时候可能需要访问由React管理的DOM元素--比如让一个节点获得焦点,滚动到它或者测量它的尺寸和位置

伤心了

去本地了

 本地node.js一下完云端就连上了

你们几个我真是选不出一个

import {useRef} from "react"

function App(){
    const myRef = useRef(null)
    const handleClick = ()=>{
        //通过ref操作原生DOM
        console.log(myRef.current.innerHTML)
        myRef.current.style.background = 'red'
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <div ref={myRef}>hello React</div>
        </div>
    )
}

export default App

 

在循环中操作 ref 可以使用回调函数写法,这是什么意思呢?

钩子在逻辑中使用是不符合ESlint规范的(哦哦哦)

import { useRef } from 'react'

function App() {
  const list = [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' },
  ]
  return (
    <div>
      hello App
      <ul>
        {list.map((item) => {
          const myRef = useRef(null)
          return <li key={item.id} ref={myRef}>{item.text}</li>
        })}
      </ul>
    </div>
  )
}

export default App

这种写法看上去运行起来没问题但是实际上会有警告

改了也没见好多少

 

我的所有ai都不太想要为我服务呢怎么

这样使用 才是合规的:

import { useRef } from 'react'

function App() {
  const list = [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' },
  ]
  return (
    <div>
      hello App
      <ul>
        {
            list.map((item)=>{
                return <li key={item.id} ref={(myRef) => {
                    if (myRef) {
                    myRef.style.background = 'red'
                }
                }}>{item
                    .text}</li>
            })
        }
      </ul>
    </div>
  )
}

组件设置ref需要forwardRef进行转发

当组件添加ref属性的时候,需要forwardRef进行转发,forwardRef让我的组件通过ref向父组件公开DOM节点

import {useRef,forwardRef} from 'react'

const MyInput = forwardRef(  function MyInput(props,ref){
    return (
        <input type="text" ref={ref}/>
    )
})
function App(){
    const ref = useRef(null)
    const handleClick = ()=>{
        ref.current.focus()
        ref.current.style.background = 'red'
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <MyInput ref={ref}/>
        </div>
    )
}
export default App

uselmperativeHandle自定义ref的暴露

输入法卡掉了

重启了

结果回来B站画中画模式又出问题了

谁给我画中画改成这样了

这个新的钩子函数用起来更加灵活,可以自己决定组件暴不暴露

import {useRef,forwardRef, useImperativeHandle} from 'react'

const MyInput = forwardRef(function MyInput(props,ref){
    const inputRef = useRef(null)
    useImperativeHandle(ref,()=>{
        return{
            focus(){
                inputRef.current.focus()
            },
            focusAndStyle(){
                inputRef.current.focus()
                inputRef.current.style.background = 'red'
            }
        }
    })
    return (
        <input type="text" ref={inputRef}/>
    )
})
function App(){
    const ref = useRef(null)
    const handleClick = ()=>{
        // ref.current.focus()
        // ref.current.style.background = 'red'
        ref.current.focusAndStyle()
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>     
            <MyInput ref={ref}/>
        </div>
    )
}
export default App

纯函数如何处理副作用useEffect基本使用

纯函数的概念回顾一下:

 什么是副作用呢?

就是函数在执行过程中对外部造成的影响,比如Ajax调用,DOM操作,与外部系统同步

在React组件中,事件操作是可以处理副作用的,但是有的时候需要初始化处理副作用,就需要useEffect钩子

import { useRef } from "react"

function App(){
    const ref = useRef(null)
    //副作用:不符合纯函数的规范
    // setTimeout(()=>{
    //     ref.current.focus()
    // },3000)
    const handleClick = ()=>{
        //副作用:符合纯函数的会犯,因为事件可以处理副作用
        ref.current.focus()
    }
    return(
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <input type="text" ref={ref} />
        </div>
    )
}

export default App
import { useEffect, useRef } from "react"

function App(){
    const ref = useRef(null)
    //副作用:不符合纯函数的规范
    // setTimeout(()=>{
    //     ref.current.focus()
    // },3000)
    const handleClick = ()=>{
        //副作用:符合纯函数的规范,因为事件可以处理副作用
        ref.current.focus()
    }
    //可以在初始的时候进行副作用操作
    // useEffect触发的时机:JSX渲染后触发
    useEffect(()=>{
        ref.current.focus()
    })
    return(
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            <input type="text" ref={ref} />
        </div>
    )
}

export default App

更新状态也会走钩子

useEffect在JSX走完之后会调用的

页面中的数字比控制台的先出来,标出了运行顺序:

import { useEffect, useRef, useState } from "react"

function App(){

    const [count,setCount] = useState(0)   //1
    useEffect(()=>{        //3
        console.log(123)
    })
    const handleClick=()=>{     //4
        setCount(count+1)   
    }
    return(         //2
        <div>
            hello App
            <button onClick={handleClick}>点击</button> {count}
        </div>
    )
}

export default App

初始渲染和更新渲染,都会触发useEffect() -->  因为每次渲染JSX后都会触发useEffect(),整个当前函数组件作用域的最后时机触发的

分开处理副作用和useEffect的依赖项使用

副作用是在某一个改变的时候触发

可以通过将依赖项数组指定为调用的第二个参数来告诉React跳过不必要的重新运行Effect

1.当依赖项为空数组的时候,只会初始触发,更新不会触发

2.ESlint会检测依赖项是否正确,包括:props,state,计算变量

import { useEffect, useState } from "react"

function App(){
    const [count,setCount] = useState(0)
    const [msg,setMsg] = useState('hello React')
    //初始的时候是所有的useEffect都会触发
    //更新的时候只有对应的依赖项发生改变的才会触发
    //内部是通过Object.is()俩判定是否改变
    useEffect(()=>{
        console.log(count)
    },[count])
    useEffect(()=>{
        console.log(msg)
    },[msg])
    //当空数组的时候,只有初始会触发,更新的时候是不会触发的
    useEffect(()=>{
        console.log(msg)
    },[])
    const handleClick=()=>{
        setCount(count+1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            {count},{msg}
        </div>
    )
}
export default App

但是有的情况是不行的:

import { useEffect, useState } from "react"

function App(){
    const [count,setCount] = useState(0)
    const [msg,setMsg] = useState('hello React')
    //这样的情况下是不可以的:
    useEffect(()=>{
        console.log(count)
    },[])
    const handleClick=()=>{
        setCount(count+1)
    }
    return (
        <div>
            hello App
            <button onClick={handleClick}>点击</button>
            {count},{msg}
        </div>
    )
}
export default App

这样的话ESlint会报警告,因为状态改变了但是它没有变,就像是本来是去小明的聊天室和小明聊天,但是现在去找小亮了结果副作用还在小明的聊天室(副作用都变了依赖项能不变吗)

尽量在useEffect内定义函数

函数也可以成为计算变量,所以也要作为依赖项

刚才输入法又卡了。。。

重启了一下之后VScode也连不上 了

准备继续配置我的本地环境

出去吃了个饭回来了

又能链接上了

import { useEffect, useState } from "react";

function App(){
    const [count,setCount] = useState(0)
    const [msg,setMsg]=useState('')
    const foo = ()=>{
        console.log(count)
    }
    useEffect(()=>{
        foo()
    },[foo])        //Object.is(1,1) -> true            Object.is(function(){},function(){}) -> false
    return (
        <div>
            hello App
        </div>
    )
}

export default App

useEffect是用Object.is检测的,如果之前定义了函数到那用了哪算改还是没改 

算改了啊

useCallback是让底层算不改

函数可以成为计算变量,所以也要作为依赖项

虽然可以利用依赖项和useCallback来解决,但是非常的不方便,最好的解决方案是把函数定义在useEffect内部

import { useCallback, useEffect, useState } from "react";

function App(){
    const [count,setCount] = useState(0)
    const [msg,setMsg]=useState('')
    const foo = useCallback(()=>{
        console.log(count)
    },[count])
    useEffect(()=>{
        foo()
    },[foo])        //Object.is(1,1) -> true            Object.is(function(){},function(){}) -> false
    return (
        <div>
            hello App
        </div>
    )
}

export default App

但是最好的解决方法还是把函数写在useEffect里面

import { useCallback, useEffect, useState } from "react";

function App(){
    const [count,setCount] = useState(0)
    const [msg,setMsg]=useState('')
    useEffect(()=>{
        const foo = ()=>{
            console.log(count)
        }
        foo()
    },[count])        
    return (
        <div>
            hello App
        </div>
    )
}

export default App

useEffect清理操作

useEffect的作用域和当前的代码是一起的(清理是整个工作的最后)

import React, { useEffect, useState } from "react";

function Chat({title}){
    useEffect(()=>{
        console.log('进入',title)
        //useEffect的清理工作
        return ()=>{
            console.log('退出',title)
        }
    },[title])
    return (
        <div>
            hello Chat
        </div>
    )
}

function App(){
    const [show,setShow] = useState(true)
    const [title,setTitle] = useState('情感聊天室')
    const handleClick =()=>{
        setShow(false)
    }
    const handleChange=(e)=>{
        setTitle(e.target.value)
    }
    return(
        <div>
            hello App
            <button onClick={handleClick}>关闭聊天室</button>
            <select value={title} onChange={handleChange}>
                <option value="情感聊天室">情感聊天室</option>
                <option value="游戏聊天室">游戏聊天室</option>
            </select>
            { show && < Chat title={title}/> }
        </div>
    )
}

export default App

这是是先退出然后再进入 的

在运行的时候记得把严格模式关掉,否则会出bug

 Promise 是 JavaScript 中用于处理异步操作的一种对象,它可以避免回调地狱,让异步代码的编写和管理更加清晰和可维护

在 JavaScript 里,异步操作是很常见的,比如网络请求、文件读取等。传统处理异步操作往往采用回调函数,但当嵌套层次过多时,代码会变得难以理解和维护,这就是所谓的 “回调地狱”。Promise 为解决这一问题而生,它代表一个异步操作的最终完成或失败,并返回其结果

Promise 有三种状态:

 
  • pending(进行中):初始状态,既不是成功,也不是失败状态。
  • fulfilled(已成功):意味着操作成功完成。
  • rejected(已失败):意味着操作失败。

 一旦 Promise 的状态从 pending 变为 fulfilled 或 rejected,就不能再改变

创建一个 Promise 对象,需要传入一个执行器(executor)函数,该函数接收两个参数:resolve 和 rejectresolve 用于将 Promise 的状态从 pending 变为 fulfilledreject 用于将状态从 pending 变为 rejected

Promise 的一个重要特性是可以进行链式调用。then 方法会返回一个新的 Promise 对象,这使得我们可以依次处理多个异步操作

添加一个定时器的话,如果时间过长还没有执行完变切换就会出问题,所以需要按时清理

import { reject } from "lodash";
import React, { useEffect, useState } from "react";

function fetchChat(title){
    const delay = title === '情感聊天室' ? 2000 : 200
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve([
                {id:1,text:title+'1'},
                {id:2,text:title+'2'},
                {id:3,text:title+'3'},
            ])
        },delay)
    })
}

function Chat({title}){
    const [list,setList]=useState([])
    useEffect(()=>{
        let ignore = false
        fetchChat(title).then((data)=>{
            if(!ignore){
                setList(data)
            }
        })
        //useEffect的清理工作
        return ()=>{
            ignore=true
        }
    },[title])
    return (
        <div>
            hello Chat
            <ul>
                {
                    list.map((item)=> <li key={item.id}>{item.text}</li> )
                }
            </ul>
        </div>
    )
}

function App(){
    const [show,setShow] = useState(true)
    const [title,setTitle] = useState('情感聊天室')
    const handleClick =()=>{
        setShow(false)
    }
    const handleChange=(e)=>{
        setTitle(e.target.value)
    }
    return(
        <div>
            hello App
            <button onClick={handleClick}>关闭聊天室</button>
            <select value={title} onChange={handleChange}>
                <option value="情感聊天室">情感聊天室</option>
                <option value="游戏聊天室">游戏聊天室</option>
            </select>
            { show && < Chat title={title}/> }
        </div>
    )
}

export default App

 当卸载组件或者更新组件的时候,可以通过useEffect来实现一些清理工作

严格模式下会检测useEffect是否实现了清理操作

初始化数据时,要注意清理操作,更简单的方式是使用第三方,比如:ahooks里的useRequest

async 用于定义一个异步函数,该函数会隐式地返回一个 Promise 对象。无论函数内部返回的是什么,最终都会被包装成一个 Promise 对象返回

async function asyncFunction() {
    // 函数体
    return '异步函数的结果';
}

// 调用 async 函数
const promise = asyncFunction();
promise.then((result) => {
    console.log(result); // 输出: 异步函数的结果
});

asyncFunction 是一个异步函数,它返回一个字符串。当调用这个函数时,实际上返回的是一个 Promise 对象,该 Promise 会在函数执行完毕后被解决(resolved),并将返回值作为结果传递给 then 方法。

await 只能在 async 函数内部使用,它用于暂停 async 函数的执行,直到其后面的 Promise 被解决(resolved)或被拒绝(rejected),并返回 Promise 的解决值。

function asyncTask() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('异步任务完成');
        }, 1000);
    });
}

async function main() {
    try {
        const result = await asyncTask();
        console.log(result); // 输出: 异步任务完成
    } catch (error) {
        console.error(error);
    }
}

main();

async 和 await 可以很方便地处理多个异步操作,避免了 Promise 链式调用带来的嵌套问题

实验性的useEffectEvent

useEffectEvent是干嘛的呢?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/react@0.0.0-experimental-e1ad4aa36-20230601/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@0.0.0-experimental-e1ad4aa36-20230601/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { useState, useEffect, experimental_useEffectEvent } = React

      function ChatRoom({ title, theme }) {
        const themeEffectEvent = experimental_useEffectEvent(() => {
          console.log('主题', theme)
        })
        useEffect(() => {
          console.log('进入', title)
          themeEffectEvent()
          return () => {
            console.log('退出', title)
          }
        }, [title])
        return <div>聊天室</div>
      }

      function App() {
        const [isDark, setIsDark] = useState(false)
        const [title, setTitle] = useState('情感聊天室')
        const handleChange = (e) => {
          setTitle(e.target.value)
        }
        const handleChange2 = (e) => {
          setIsDark(e.target.checked)
        }
        return (
          <div>
            hello App
            <select value={title} onChange={handleChange}>
              <option value="情感聊天室">情感聊天室</option>
              <option value="体育聊天室">体育聊天室</option>
            </select>
            <input type="checkbox" checked={isDark} onChange={handleChange2} />黑暗主题
            <ChatRoom title={title} theme={isDark ? 'dark' : 'light'} />
          </div>
        )
      }
      ReactDOM.createRoot(document.getElementById('root')).render(<App />)

    </script>
  </body>
</html>
import { useEffect, useState } from "react"

function Chat({ title, theme }) {
  useEffect(() => {
    console.log('进入', title)
    console.log('主题', theme)
    return () => {
      console.log('退出', title)
    }
  }, [title, theme])
  return (
    <div>
      hello Chat
    </div>
  )
}


function App() {
  const [show, setShow] = useState(true)
  const [title, setTitle] = useState('情感聊天室')
  const [isDark, setIsDark] = useState(false)
  const handleClick = () => {
    setShow(false)
  }
  const handleChange = (e) => {
    setTitle(e.target.value)
  }
  const handleChange2 = (e) => {
    setIsDark(e.target.checked)
  }
  return (
    <div>
      hello App
      <button onClick={handleClick}>关闭聊天室</button>
      <select value={title} onChange={handleChange}>
        <option value="情感聊天室">情感聊天室</option>
        <option value="体育聊天室">体育聊天室</option>
      </select>
      <input type="checkbox" checked={isDark} onChange={handleChange2} />黑暗主题
      { show && <Chat title={title} theme={ isDark ? 'dark' : 'light' } /> }
    </div>
  )
}

export default App

写完了走了


http://www.kler.cn/a/565459.html

相关文章:

  • Python常见面试题的详解22
  • 解决vmware虚拟机下 kali 无root权限问题
  • Midscene.js - AI驱动,轻松实现UI自动化
  • 【数据结构进阶】哈希表
  • springboot411-基于Java的自助客房服务系统(源码+数据库+纯前后端分离+部署讲解等)
  • 【01游戏——DFS】
  • Linux驱动学习(四)--字符设备注册
  • 工作中遇到的EXCEL小问题:多行有间隔符的合并
  • 广东GZ033-任务E:数据可视化(15 分)-用柱状图展示销售金额最高的6 个月
  • Golang适配达梦数据库连接指定模式
  • Yolo各个系列的模型、ResNet、Pyrimid network、VGG、PointNet、mobilenet模型
  • kafka小白基础知识
  • Leetcode2296:设计一个文本编辑器
  • RabbitMQ系列(七)基本概念之Channel
  • 在MacOS上打造本地部署的大模型知识库(一)
  • Springboot 事件通知监听
  • WebSocket相关技术
  • vue2版本elementUI的table分页实现多选逻辑
  • 【AHK】资源管理器自动化办公实例/自动连点设置
  • JavaScript系列(86)--现代构建工具详解