什么是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 = 0const 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 = nullconst 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操作原生DOMconsole.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) //1useEffect(()=>{ //3console.log(123)})const handleClick=()=>{ //4setCount(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(){}) -> falsereturn (<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(){}) -> falsereturn (<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 : 200return 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 = falsefetchChat(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 } = Reactfunction 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
写完了走了