useState
useState用于在函數(shù)組件中聲明和管理狀態(tài)
它接受初始狀態(tài),并返回一個(gè)狀態(tài)變量和一個(gè)更新?tīng)顟B(tài)的函數(shù)
通過(guò)調(diào)用更新?tīng)顟B(tài)的函數(shù),可以改變狀態(tài)的值并觸發(fā)組件的重新渲染
import { useState } from "react"
function App() {
const [obj, setObj] = useState({
a: 1,
b: 2
})
const [arr, setArr] = useState([])
const updateObjValue = () => {
// 更新引用值的狀態(tài),必須傳遞一個(gè)新的引用
setObj(prev => {
return {
...obj,
b: 3
}
})
setArr(prev => [...arr, 1])
}
return (
<div onClick={updateObjValue}>
a: {obj.a}
b: {obj.b}
數(shù)組:{arr}
</div>
)
}
export default App;
useEffect
React的useEffect鉤子可以讓開(kāi)發(fā)者在函數(shù)組件中管理副作用。
副作用操作是指那些與組件渲染無(wú)關(guān)的操作,例如訪問(wèn)網(wǎng)絡(luò)、獲取瀏覽器窗口大小、訪問(wèn)本地存儲(chǔ)等。這些操作一般會(huì)產(chǎn)生一些副作用,例如更新組件狀態(tài)、改變?yōu)g覽器地址等。
使用useEffect可以在組件渲染后執(zhí)行副作用操作,并且可以根據(jù)需要設(shè)置條件,只在滿足條件時(shí)執(zhí)行副作用操作,從而避免不必要的計(jì)算和網(wǎng)絡(luò)請(qǐng)求。
useEffect接收兩個(gè)參數(shù):第一個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),該函數(shù)會(huì)在組件渲染之后執(zhí)行,用于執(zhí)行副作用操作。
如果需要在組件卸載時(shí)執(zhí)行一些清理操作,可以在該函數(shù)中返回一個(gè)清理函數(shù),React會(huì)在組件卸載時(shí)調(diào)用該函數(shù)(如清除定時(shí)器等)。
第二個(gè)參數(shù)是一個(gè)可選數(shù)組,用于指定當(dāng)依賴項(xiàng)發(fā)生變化時(shí)是否需要重新執(zhí)行effect函數(shù)。
如果第二個(gè)參數(shù)為空數(shù)組,則表示effect只會(huì)在組件掛載和卸載時(shí)執(zhí)行一次;如果第二個(gè)參數(shù)不為空數(shù)組,每當(dāng)指定的依賴項(xiàng)發(fā)生變化時(shí),effect函數(shù)都會(huì)重新執(zhí)行。
// 監(jiān)聽(tīng)count的變化,重新渲染
import React, { useState, useEffect } from 'react';
function MyComponent(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
請(qǐng)求網(wǎng)絡(luò)
import { useState, useEffect } from 'react';
function Example() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<div>
{data && <h1>{data.title}</h1>}
</div>
);
}
通過(guò)useEffect來(lái)獲取數(shù)據(jù)
import {useEffect, useState} from "react";
const UseEffectDemo = () => {
const [list, setList] = useState([])
const [id, setId] = useState('5433d5e4e737cbe96dcef312')
const [num, setNum] = useState(0)
const [info, setInfo] = useState([])
useEffect(()=>{
const requestList = async () => {
try {
const response = await fetch('https://cnodejs.org/api/v1/topics')
const res = await response.json()
console.log(res['data'])
setList(res['data'])
} catch (err) {
if (err) throw Error
}
}
requestList().then((rs)=>{
console.log('requestList')
})
},[])
useEffect(()=>{
const requestInfo = async () => {
try {
const response = await fetch('https://cnodejs.org/api/v1/topic/' + id)
const res = await response.json()
setInfo(res['data'])
} catch (err) {
if (err) throw Error
}
}
requestInfo().then((rs)=>{
console.log('requestInfo')
})
},[id])
return (
<>
<h2 id='title'>UseEffect Demo</h2>
<h4>{id}</h4>
<div style={{display:'flex'}}>
<ul>
{
list.map((item, index)=>{
return (
<li
key={item.id}
style={{background:index === num ? 'skyblue':'white', cursor:"pointer"}}
onClick={()=>{
setId(item.id)
setNum(index)
}}
>{item.id}</li>
)
})
}
</ul>
<div dangerouslySetInnerHTML={{__html: info.content}}>
</div>
</div>
</>
)
}
export default UseEffectDemo
useEffect是異步執(zhí)行的,useLayoutEffect是同步執(zhí)行的。
useLayoutEffect是當(dāng)瀏覽器把內(nèi)容渲染到頁(yè)面之前執(zhí)行,而useEffect是當(dāng)瀏覽器把內(nèi)容渲染到頁(yè)面之后執(zhí)行。盡可能使用標(biāo)準(zhǔn)的useEffect以避免阻塞頁(yè)面更新。
useEffect的運(yùn)行規(guī)則:組件首次渲染工作完成并將真實(shí)dom生成到頁(yè)面以后,將對(duì)應(yīng)的回調(diào)函數(shù)推入異步隊(duì)列等待執(zhí)行
useLayoutEffect的運(yùn)行規(guī)則:組件首次渲染工作完成并將真實(shí)dom生成到頁(yè)面以后,將對(duì)應(yīng)的回調(diào)函數(shù)推入同步隊(duì)列等待執(zhí)行
正常情況全部使用useEffect,只有逼不得已的情況才考慮是否使用useLayoutEffect
import { useState, useEffect } from "react";
export default function TopicList() {
const [topicList, setTopicList] = useState([])
useEffect(() => {
const requestList = async () => {
try {
const response = await fetch('https://cnodejs.org/api/v1/topics')
const res = await response.json()
console.log(res['data'])
setTopicList(res['data'])
} catch (err) {
if (err) throw Error
}
}
requestList().then((rs)=>{
console.log('requestList')
})
const topicListDom = document.getElementsByClassName("topic-list-wrapper")[0]
console.log('topicListDom', topicListDom)
}, [])
return (
<div className="topic-list-wrapper">
{
topicList.map(topic => (
<div key={topic.id}>{topic.title}</div>
)
)
}
</div>
)
}
清除定時(shí)器
import { useState, useEffect } from "react";
export default function Tick() {
const [tickTime, setTickTime] = useState(100)
useEffect(() => {
let timer = setInterval(() => {
console.log("tickTime working")
setTickTime(prev => prev - 1)
}, 1000);
return () => {
clearInterval(timer)
timer = null
}
}, [])
return (
<div>倒計(jì)時(shí): {tickTime}</div>
)
}
useReducer
useReducer是另一種讓函數(shù)組件保存狀態(tài)的方式
它接受一個(gè) reducer
函數(shù)和一個(gè)初始狀態(tài),并返回當(dāng)前狀態(tài)和 dispatch
函數(shù)
import { useReducer } from "react";
export default function ReducerDemo() {
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return state + 1
case 'decrement':
return state + 1
}
return state;
}
const [count, dispatch] = useReducer(reducer, 0)
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch({ type: 'increment' })}>plus</button>
</div>
)
}
useCallBack
每次組件的重新渲染都意味著內(nèi)部所有的引用值都會(huì)被重新構(gòu)建
useCallBack用來(lái)長(zhǎng)期穩(wěn)定的維護(hù)某一個(gè)函數(shù)的引用,它會(huì)將函數(shù)創(chuàng)建后的引用保存,當(dāng)函數(shù)組件下一次重新渲染時(shí),它會(huì)直接返回之前保存的引用,而不是重新創(chuàng)建引用
useCallBack只在創(chuàng)建函數(shù)引用的時(shí)候使用
import {useState, memo, useCallback} from "react";
const Child = memo(({value,change}) => {
console.log('----re-render----')
return (
<input type="text" value={value} onChange={change}/>
)
})
const UseCallback = () => {
const [v1, setV1] = useState('')
const [v2, setV2] = useState('')
const onChange1 = useCallback((e)=>{
setV1(e.target.value)
}, [])
const onChange2 = useCallback((e)=>{
setV2(e.target.value)
}, [])
return (
<>
<h2>UseCallback</h2>
<Child value={v1} change={onChange1}/>
<Child value={v2} change={onChange2}/>
</>
)
}
export default UseCallback
useMemo
useMemo類似于計(jì)算屬性
useCallBack就是useMemo實(shí)現(xiàn)的,用來(lái)做緩存的
useCallBack第一個(gè)參數(shù)也是一個(gè)函數(shù),并不會(huì)在聲明時(shí)被React直接執(zhí)行;該函數(shù)會(huì)在組件渲染過(guò)程中被 useMemo 鉤子調(diào)用,將計(jì)算結(jié)果進(jìn)行緩存
第二個(gè)參數(shù)是依賴項(xiàng),當(dāng)依賴項(xiàng)變化時(shí),React會(huì)重新執(zhí)行對(duì)應(yīng)的第一個(gè)參數(shù),然后拿到最新的返回值,再次進(jìn)行緩存
業(yè)內(nèi)都是使用useCallBack去緩存函數(shù)
memo方法:
性能優(yōu)化,如果本組件中的數(shù)據(jù)沒(méi)有發(fā)生變化,阻止組件更新
import { memo } from "react";
function MemoDemo() {
console.log('memo rendering...')
return (
<div>memo data</div>
)
}
export default memo(MemoDemo)
自定義hooks
使用自定義 hook 時(shí),需要遵循以下規(guī)則:
- 自定義 hook 必須是一個(gè)函數(shù),名稱必須以“use”為前綴。
- 自定義 hook 可以調(diào)用其他鉤子或普通函數(shù)。
- 自定義 hook 應(yīng)該使用 useState、useEffect 等 React 鉤子創(chuàng)建狀態(tài)和處理副作用。
- 可以將自定義鉤子與 useContext 和 useReducer 配合使用來(lái)實(shí)現(xiàn)更復(fù)雜的行為。
下面是一個(gè)示例自定義 hook,用于檢查用戶是否擁有特定的權(quán)限:
import { useState, useEffect } from 'react';
function useUserPermissions(permission) {
const [hasPermission, setHasPermission] = useState(false);
useEffect(() => {
// 模擬 Ajax 請(qǐng)求,獲取用戶權(quán)限
const userPermissions = ['admin', 'editor'];
if (userPermissions.includes(permission)) {
setHasPermission(true);
} else {
setHasPermission(false);
}
}, [permission]);
return hasPermission;
}
在其他組件中使用 hook 檢查用戶是否擁有訪問(wèn)特定頁(yè)面或功能的權(quán)限
import React from 'react';
import useUserPermissions from './useUserPermissions';
function Dashboard() {
const hasAccess = useUserPermissions('admin');
return (
<div>
{hasAccess ? (
<h1>Welcome to the Admin Dashboard!</h1>
) : (
<h1>Sorry, you do not have access to this page.</h1>
)}
</div>
);
}
顯示loading效果
新建文件useRequestLoadingDispatcher.js
import { useState } from "react";
export default function useRequestLoadingDispatcher() {
const [loading, setLoading] = useState(false)
const executeRequest =async (promiseFn) => {
setLoading(true)
console.log('loading data........................')
await promiseFn()
setLoading(false)
console.log('init finish')
}
return {loading, executeRequest}
}
使用useRequestLoadingDispatcher
import { useState, useEffect } from "react";
import TopicItem from "../TopicItem";
import useRequestLoadingDispatcher from "../hooks/useRequestLoadingDispatcher";
export default function TopicList() {
const [topicList, setTopicList] = useState([])
const {loading, executeRequest} = useRequestLoadingDispatcher()
const fetchFromServer = async () => {
// 延時(shí)模擬加載中
const delay = async (duration = 2000) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, duration)
})
}
executeRequest(async () => {
try {
const response = await fetch('https://cnodejs.org/api/v1/topics')
const res = await response.json()
console.log(res['data'])
await delay(2000)
setTopicList(res['data'])
} catch (err) {
if (err) throw Error
}
})
}
useEffect(() => {
fetchFromServer()
}, [])
return (
<div className="topic-list-wrapper">
{ loading ? <div>數(shù)據(jù)加載中</div> : (
topicList.map(topic => (
<TopicItem {...topic}></TopicItem>
)
)
)
}
</div>
)
}
強(qiáng)制刷新useForceUpdate
import { useState } from "react";
export default function useForceUpdate() {
const [_, setForceObj] = useState({})
const forceUpdate = () => {
setForceObj({})
}
return forceUpdate;
}
使用強(qiáng)制刷新
import useForceUpdate from "../hooks/useForceUpdate"
export default function ForceUpdateTest() {
console.log('do force update')
const forceUpdate = useForceUpdate()
return (
<button onClick={forceUpdate}>force update</button>
)
}
useRef
構(gòu)建一個(gè)狀態(tài)出來(lái),但是這個(gè)狀態(tài)是直接脫離React控制的,也不會(huì)造成頁(yè)面的重新渲染,同時(shí)狀態(tài)還不會(huì)因?yàn)榻M件的重新渲染而被初始化
import { useState, useCallback, useRef } from "react";
export default function Ticker() {
const [timeCount, setTimeCount] = useState(60)
const timerIdRef = useRef(null)
// useRef會(huì)返回一個(gè)對(duì)象,里面有一個(gè)current屬性
// ref是可讀可寫的
console.log("timerIdRef", timerIdRef)
const startTick = useCallback(() => {
timerIdRef.current = setInterval(()=>{
setTimeCount(prev => prev - 1)
}, 1000)
})
const stopTick = useCallback(() => {
if (timerIdRef.current) clearInterval(timerIdRef.current)
})
return (
<>
<button onClick={startTick}>start</button>
<button onClick={stopTick}>stop</button>
<span>{timeCount}</span>
</>
)
}
使用useRef獲取DOM元素
import { useCallback, useEffect, useRef } from "react"
export default function TestInput() {
const inputElementRef = useRef(null)
useEffect(() => {
inputElementRef.current = document.getElementsByClassName('input-example')[0]
}, [])
const handleClick = useCallback(() => {
inputElementRef.current.focus()
})
return (
<div>
<input className="input-example"></input>
<button onClick={handleClick}>click me</button>
</div>
)
}
useRef可以直接作用在dom上,它使用useEffect去獲取真實(shí)dom并且賦值
import { useCallback, useRef } from "react"
export default function TestInput() {
const inputElementRef = useRef(null)
const handleClick = useCallback(() => {
inputElementRef.current.focus()
})
return (
<div>
<input ref={inputElementRef} className="input-example"></input>
<button onClick={handleClick}>click me</button>
</div>
)
}
fowardRef
fowardRef是一個(gè)高階組件
高階組件:接收一個(gè)組件作為參數(shù),返回一個(gè)新的組件
fowardRef給函數(shù)組件擴(kuò)展了一個(gè)ref屬性
TestInput.jsx
import { forwardRef } from "react"
// 給組件掛載ref屬性,必須使用forwardRef
function TestInput(props, parentRef) {
return (
<div>
<input ref={parentRef} className="input-example"></input>
</div>
)
}
export default forwardRef(TestInput)
App.js
forwardRef一般都是和組件ref連用的,不會(huì)單獨(dú)使用
import { useCallback, useRef } from "react";
import TestInput from "./components/TestInput";
function App() {
const testInputRef = useRef(null)
const handleClick = useCallback(() => {
testInputRef.current.focus()
})
return (
<div>
<TestInput ref={testInputRef}/>
<button onClick={handleClick}>click me</button>
</div>
)
}
export default App;
useImperativeHandle
用于向父組件暴露子組件實(shí)例的特定函數(shù)。它通常與forwardRef一起使用,在子組件中定義外部可以調(diào)用的實(shí)例方法。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-634905.html
- 在子組件中使用forwardRef,將ref作為第二個(gè)參數(shù)傳遞給子組件;第三個(gè)參數(shù)是依賴項(xiàng)
const ChildComponent = forwardRef((props, ref) => {
// 定義需要暴露給父組件的方法
useImperativeHandle(ref, () => ({
// 這里定義需要暴露給父組件的方法
focus: () => {
// 在這里編寫具體的邏輯
// 注意:這里的 focus 是一個(gè)示例,你可以定義任何你需要的方法
// 你可以在該方法內(nèi)部執(zhí)行子組件的某個(gè)操作
// 這個(gè)方法會(huì)被父組件調(diào)用
},
// 定義其他的方法...
}));
// 渲染子組件的 JSX
return <div>子組件</div>;
});
export default ChildComponent;
- 在父組件中創(chuàng)建一個(gè)ref對(duì)象,并將其傳遞給子組件:
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
// 創(chuàng)建 ref 對(duì)象
const childRef = useRef();
// 在父組件中可以通過(guò)調(diào)用子組件的方法來(lái)訪問(wèn)子組件的實(shí)例
const handleClick = () => {
childRef.current.focus(); // 調(diào)用子組件的 focus 方法
};
return (
<div>
<button onClick={handleClick}>調(diào)用子組件方法</button>
<ChildComponent ref={childRef} />
</div>
);
};
export default ParentComponent;
useContext
允許組件之間通過(guò)除了props以外的情況去共享數(shù)據(jù)
如果組件屬性傳遞超過(guò)了4層,可以考慮使用上下文
上下文大多數(shù)是用來(lái)做全局?jǐn)?shù)據(jù)管理的文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-634905.html
- 創(chuàng)建上下文:
context/themeContext.js
import { createContext } from "react";
const ThemeContext = createContext("light")
export default ThemeContext
- 給自定義上下文設(shè)置值
import { useCallback, useState } from "react";
import ThemeContext from "./context/themeContext";
import TestInput from "./components/TestInput";
function App() {
const [theme, setTheme] = useState("light")
const changeTheme = useCallback(() => {
setTheme(prev => {
if (prev === 'light') return "dark"
return "light"
})
})
return (
<div className="wrapper">
<ThemeContext.Provider value = {theme}>
<TestInput />
<button onClick={changeTheme}>change theme</button>
</ThemeContext.Provider>
</div>
)
}
export default App;
- 在組件中獲取上下文中的值
import { useContext } from "react"
import ThemeContext from "../../context/themeContext"
export default function TestInput(props) {
const contextValue = useContext(ThemeContext)
return (
<div style={{background: contextValue === 'light' ? "#fff" : "#666"}}>
<input></input>
</div>
)
}
到了這里,關(guān)于React18 hook學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!