import React, { useEffect, useState } from 'react'; hook 是react 16.8的新增特性 ,他可以讓你不在編寫class的情況下shiystate以及react的特性 Hooks的出現,首先解決了以下問題: 告別了令人疑惑的生命周期 告別類組 ...
import React, { useEffect, useState } from 'react';
hook 是react 16.8的新增特性 ,他可以讓你不在編寫class的情況下shiystate以及react的特性 Hooks的出現,首先解決了以下問題:- 告別了令人疑惑的生命周期
- 告別類組件中煩人的this
- 告別繁重的類組件,回歸到了熟悉的函數組件
useState
1.基礎使用
import { useState } from 'react' function App() { // 參數:狀態初始值比如,傳入 0 表示該狀態的初始值為 0 // 返回值:數組,包含兩個值:1 狀態值(state) 2 修改該狀態的函數(setState) const [count, setCount] = useState(0); // 修改count內容 const modifyEvent = () => { setCount(count + 1) } return ( <button onClick={() => modifyEvent()}>{count}</button> ) } export default App
2.狀態的讀取和修改執行流程與邏輯
讀取狀態:該方式提供的狀態,是函數內部的局部變數,可以在函數內的任意位置使用 修改狀態:1.setCount是一個函數,參數表示最新的狀態值
2.調用該函數後,將使用新值替換舊值
3.修改狀態後,由於狀態發生變化,會引起試圖變化 註意
事項:修改狀態的時候,一定要使用新的狀態替換舊的狀態,不能直接修改舊的狀態,尤其是引用類型
3. 組件的更新過程
函數組件使用 useState hook 後的執行過程,以及狀態值的變化1.組件第一次渲染
- 從頭開始執行該組件中的代碼邏輯
- 調用 useState(0) 將傳入的參數作為狀態初始值,即:0
- 渲染組件,此時,獲取到的狀態 count 值為: 0
2.組件第二次渲染
- 點擊按鈕,調用 setCount(count + 1) 修改狀態,因為狀態發生改變,所以,該組件會重新渲染
- 組件重新渲染時,會再次執行該組件中的代碼邏輯
- 再次調用 useState(0) ,此時 React 內部會拿到最新的狀態值而非初始值,比如,該案例中最新的狀態值為 1
- 再次渲染組件,此時,獲取到的狀態 count 值為:1
import { useState } from 'react' function App() { const [count, setCount] = useState(0) // 在這裡可以進行列印 console.log(count,'渲染了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
4.使用規則
1.useState 函數可以執行多次,每次執行互相獨立,每調用一次為函數組件提供一個狀態function List(){ // 以字元串為初始值 const [name, setName] = useState('cp') // 以數組為初始值 const [list,setList] = useState([]) }
2.useState 註意事項
- 只能出現在函數組件或者其他hook函數中
- 能嵌套在if/for/其它函數中(react按照hooks的調用順序識別每一個hook)
useEffect
1.理解函數副作用
副作用是相對於主作用來說的,一個函數除了主作用,其他的作用就是副作用。對於 React 組件來說,主作用就是根據數據(state/props)渲染 UI,除此之外都是副作用(比如,手動修改 DOM) 常見的副作用:- 數據請求 ajax發送
- 手動修改dom
- localstorage操作
2. 基礎使用
下麵案例說明,函數組件儲存了當前state所有狀態,函數初次就會觸發他們兩個的載入,另外當某一個發生改變了 useEffect和函數都會被重新執行載入
import { useEffect, useState } from 'react' function App() { const [count, setCount] = useState(0) useEffect(()=>{ // 修改了dom數據後 userffect函數被副作用重新執行 console.log('執行了副作用函數') }); // 函數組件也會被重新執行 console.log('函數組件被重新執行了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
3.useEffect依賴項和控制執行的時機
由於 組件首次渲染執行一次,以及不管是哪個狀態更改引起組件更新時都會重新執行 添加空數組依賴import { useEffect, useState } from 'react' function App() { const [count, setCount] = useState(0) useEffect(()=>{ // 修改了dom數據後 userffect函數不會在被觸發,只有首次載入函數才會執行一次 console.log('執行了副作用函數') },[]); //修改了dom數據後 函數組件會被重新執行 console.log('函數組件被重新執行了') return ( <button onClick={() => { setCount(count + 1) }}>{count}</button> ) } export default App
添加特定項作為依賴
副作用函數在首次渲染時執行,在依賴項發生變化時重新執行
給useEffect添加特定的依賴項,當這個依賴性的state發生改變,useEffect與函數組件都會重新渲染被執行,由於第二個參數是依賴項所以是數組可以添加多個依賴項
沒有useEffect添加的特定的依賴項,就不會觸發useEffect函數,只會觸發組件的渲染函數
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => { console.log('副作用執行了') }, [count]) console.log('組件被執行了') return ( <> <button onClick={() => { setCount(count + 1) }}>{count}</button> <button onClick={() => { setName('cp') }}>{name}</button> </> ) }
清理副作用
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('zs') useEffect(() => { console.log('副作用執行了') return () => { alert(1) console.log('執行了清楚副作用,組件卸載的時候執行') } }, [count]) console.log('組件被執行了') return ( <> <button onClick={() => { setCount(count + 1) }}>{count}</button> <button onClick={() => { setName('cp') }}>{name}</button> </> ) }
另外一般一個 useEffect 只用來處理一個功能,有多個功能時,建議使用多個 useEffect
useMemo(性能優化)
解決函數組件的性能問題,比如子組件重覆執行問題,每次渲染都進行高開銷的計算
// 子組件 function Sub(props) { console.log("Sub render"); let { number, onClick } = props return ( <button onClick={onClick}>{number}</button> ) } // 父組件 function Test() { let [value, setValue] = useState('') let [number, setNumber] = useState(0) const addClick = () => setNumber(number + 1) return <> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> <Sub number={number} onClick={addClick} /> </> } export default Test;
子組件依賴的只有number ,理想情況下只希望number變化時觸發子組件重新渲染
但實際是在輸入框內的值發生變化,子組件也會重新渲染 如果子組件的邏輯較複雜,就是無意義的大量計算,浪費資源
// 子組件 function Sub(props) { console.log("Sub render"); let { number, onClick } = props return ( <button onClick={onClick}>{number}</button> ) } // 父組件 function Test() { let [value, setValue] = useState('') let [number, setNumber] = useState(0) const addClick = () => setNumber(number + 1); // 使用useMemo記住計算後的值,只有當依賴number變數發生變化,才會重新計運算元組件內容 const MemoSub = useMemo( () => <Sub data={number} onClick={addClick} />, [number] // 只有 number 變化才重新計算 MenoSub ) return <> <input type="text" value={value} onChange={(e) => setValue(e.target.value)} /> {MemoSub} </> } export default Test;
useCallback(性能優化)
接收兩個參數:回調函數和依賴項數組。回調函數是需要緩存的函數,依賴項數組用於確定何時需要重新創建函數實例。
當依賴項數組中的任何一個值發生變化時,useCallback 將返回一個新的函數實例,否則它將返回之前緩存的函數實例
import { useState, useCallback } from 'react'; function MyComponent() { const [count, setCount] = useState(0); // 使用 useCallback 緩存 handleClick 函數 const handleClick = useCallback(() => { setCount(count + 1); }, [count]); return ( <div> <p>You clicked {count} times</p> {/* 在按鈕上使用緩存的 handleClick 函數 */} <button onClick={handleClick}>Click me</button> </div> ); }
在這個例子中,我們使用 useCallback 來緩存回調函數 handleClick, 將其緩存以避免在每次重新渲染組件時創建新的函數實例。
同時,在按鈕上使用了緩存的 handleClick 函數,以確保點擊按鈕時調用的是緩存的函數實例。我們還將 count 添加到依賴項數組中,以確保每當 count 發生變化時,handleClick 都會被重新創建。
useCallback 和 useMomeo 的區別
1.useCallback 和 useMemo 都是用於性能優化的 React 鉤子函數,它們都可以避免不必要的重新計算或重新渲染。雖然它們看起來很相似,但它們有幾個重要的區別。
2.首先,useCallback 返回一個緩存的回調函數,而 useMemo 返回一個緩存的值。這意味著 useCallback 的主要作用是為一個函數創建緩存,而 useMemo 的主要作用是緩存一個值
3.最後,它們的使用場景也不同。useCallback 適用於優化回調函數,避免不必要的重新渲染,並傳遞給子組件。而 useMemo 適用於優化計算開銷較大的值,如大型數組或對象的計算
useRef
useRef 可以緩存所有數據類型,更新的數據不會隨著組件的重新渲染而重置,會一直保持最新狀態的內容,
但是保存的數據類型 無法在ui渲染頁面上使用,只能作為一個狀態進行儲存
也可以綁定給一個元素標簽獲取dom進行操作
function Test() { /* 保存 DOM */ const inputEl = useRef() const onClick = () => { console.log(inputEl); // 對象類型,只有一個 current 屬性指向指定DOM inputEl.current.innerHTML = '2asdasd sd阿薩德' } return <div> <div ref={inputEl}></div> <button onClick={onClick}>click me!!!</button> <br /> </div> }
useContext
usecontext React 16.3本中新引入的一個特性,它可以讓組件之間共用數據十分方便。它屬於 React Context API(上下文API),可以讓組件層級之間自由傳遞數據,而使用Context API可以極大地提高組件之間的可復用性。
使用 useContext以使組件樹中的任何組件訪問到 cntext值,無論它是何種層級的,而且更方便的是,不需要利用props行傳遞,而只需要一行代碼即可
下麵案例是同一組件下多個 子組件的上下文通訊
import React, { createContext, useContext } from 'react'; const ThemeContext = createContext('light') // 父組件 function Test() { return ( <ThemeContext.Provider value="dark"> <Toolbar theme="dark" /> </ThemeContext.Provider> ) } // 子組件 function Toolbar(props) { return ( // 中間的組件再也不必指明往下傳遞 theme 了。 <div> <Button /> </div> ) } // 子組件中的子組件 function Button() { // 指定 contextType 讀取當前的 themecontext。 // React 會往上找到最近的 theme Provider,然後使用它的值。 // 在這個例子中,當前的 theme 值為 “dark”。 const theme = useContext(ThemeContext) return <button>{ theme }</button> }
垮文件使用usecontext 進行通訊
父組件
import React, { useState, createContext } from 'react' import Counter from './Counter' export const countContext = createContext() export default function Example4() { const [count, setCount] = useState(0) return ( <div> <p>你點擊了{count}次</p> <button onClick={()=>{setCount(count+1)}}>點擊</button> <countContext.Provider value={count}> <Counter /> </countContext.Provider> </div> ) }
子組件
import React, { useContext } from 'react' import { countContext } from './Example4' function Counter() { let count = useContext(countContext) return ( <div> <h2>{count}</h2> </div> ) } export default Counter
這樣在多級組件使用就減少使用redux,useContext確實好用,起碼跨級傳遞數據不用那麼麻煩了
完結,有不足歡迎補充。