REACT学习第三幕--沉睡花园
- 电脑硬件
- 2025-09-17 00:21:02

什么是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 AppuseEffect是用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 和 reject。resolve 用于将 Promise 的状态从 pending 变为 fulfilled,reject 用于将状态从 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 链式调用带来的嵌套问题
实验性的useEffectEventuseEffectEvent是干嘛的呢?
<!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=" unpkg /react@0.0.0-experimental-e1ad4aa36-20230601/umd/react.development.js"></script> <script src=" unpkg /react-dom@0.0.0-experimental-e1ad4aa36-20230601/umd/react-dom.development.js"></script> <script src=" unpkg /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写完了走了
REACT学习第三幕--沉睡花园由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“REACT学习第三幕--沉睡花园”