react之Hooks的介紹、useState與useEffect副作用的使用
- 一、Hooks的基本介紹
- 二、useState的使用
- 2.1 簡單使用
- 2.2 數組結構簡化
- 2.3 狀態的讀取和修改
- 2.3 組件的更新過程
- 三、useEffect的使用
- 3.1 副作用介紹
- 3.2 基本使用
- 3.3 依賴
- 3.4 不要對依賴項撒謊
- 3.5 依賴項可以是空數組
- 3.6 清理工作
- 3.7 useEffect的 4 種使用使用方式
- 3.8 發送請求
- 3.9 axios請求本地json數據
一、Hooks的基本介紹
Hooks 是 React v16.8 中的新增功能
為函數組件提供狀態、生命周期等原本 class 組件中提供的 React 功能
可以理解為通過 Hooks 為函數組件鉤入 class 組件的特性
注意:Hooks 只能在函數組件中使用,自此,函數組件成為 React 的新寵兒
可以在項目中同時使用hooks和class
二、useState的使用
2.1 簡單使用
一個 Hook 就是一個特殊的函數,讓你在函數組件中獲取狀態等 React 特性
useState使用場景:當你想要在函數組件中,使用組件狀態時,就要使用 useState Hook 了
useState作用:為函數組件提供狀態(state)
import { useState } from 'react'const Count = () => { // stateArray 是一個數組const stateArray = useState(0)const state = stateArray[0]const setState = stateArray[1]return (<div>{/* 展示狀態值 */}<h1>狀態為:{state}</h1>{/* 點擊按鈕,讓狀態值 +1 */}<button onClick={() => setState(state + 1)}>+1</button></div>)
}
2.2 數組結構簡化
import { useState } from 'react'const Count = () => {// 解構:const [count, setCount] = useState(0)return (<div><h1>計數器:{state}</h1><button onClick={() => setState(state + 1)}>+1</button></div>)
}
2.3 狀態的讀取和修改
讀取狀態:useState 提供的狀態,是函數內部的局部變量,可以在函數內的任意位置使用
const Counter = () => {const [user, setUser] = useState({ name: 'jack', age: 18 })return (<div><p>姓名:{user.name}</p><p>年齡:{user.age}</p></div>)
}
修改狀態:
setCount(newValue) 是一個函數,參數表示:新的狀態值
調用該函數后,將使用新的狀態值替換舊值
修改狀態后,因為狀態發生了改變,所以,該組件會重新渲染
const Counter = () => {const [user, setUser] = useState({ name: 'jack', age: 18 })const onAgeAdd = () => {setUser({...user,age: user.age + 1})}return (<div><p>姓名:{user.name}</p><p>年齡:{user.age}</p><button onClick={onAgeAdd}>年齡+1</button></div>)
}
- 修改狀態的時候,一定要使用新的狀態替換舊的狀態
2.3 組件的更新過程
函數組件使用 useState hook 后的執行過程,以及狀態值的變化:
組件第一次渲染:
1.從頭開始執行該組件中的代碼邏輯
2.調用 useState(0) 將傳入的參數作為狀態初始值,即:0
3.渲染組件,此時,獲取到的狀態 count 值為: 0
組件第二次渲染:
1.點擊按鈕,調用 setCount(count + 1) 修改狀態,因為狀態發生改變,所以,該組件會重新渲染
2.組件重新渲染時,會再次執行該組件中的代碼邏輯
3.再次調用 useState(0),此時 React 內部會拿到最新的狀態值而非初始值,比如,該案例中最新的狀態值為 1
4.再次渲染組件,此時,獲取到的狀態 count 值為:1
- useState 的初始值(參數)只會在組件第一次渲染時生效
核心代碼
import { useState } from 'react'const Count = () => {const [count, setCount] = useState(0)return (<div><h1>計數器:{count}</h1><button onClick={() => setCount(count + 1)}>+1</button></div>)
}
三、useEffect的使用
3.1 副作用介紹
內容:
-
使用場景:當你想要在函數組件中,處理副作用(side effect)時,就要使用 useEffect Hook 了
-
作用:處理函數組件中的副作用(side effect)
-
問題:副作用(side effect)是什么?
-
回答:在計算機科學中,如果一個函數或其他操作修改了其局部環境之外的狀態變量值,那么它就被稱為有副作用
類比,對于 999 感冒靈感冒藥來說: -
(主)作用:用于感冒引起的頭痛,發熱,鼻塞,流涕,咽痛等
-
副作用:可見困倦、嗜睡、口渴、虛弱感
-
理解:副作用是相對于主作用來說的,一個功能(比如,函數)除了主作用,其他的作用就是副作用
-
對于 React 組件來說,主作用就是根據數據(state/props)渲染 UI,除此之外都是副作用(比如,手動修改 DOM)
-
常見的副作用(side effect):數據(Ajax)請求、手動修改 DOM、localStorage、console.log 操作等
總結:
? 對于react組件來說,除了渲染UI之外的其他操作,都可以稱之為副作用
let a = 1const Count = () => {const [count, setCount] = useState(0)const handleClick = () => {setCount(count + 1)}console.log('aaa')axios.post('http://xxx')localStorage.setItem()a = 2return (<div><h1>計數器:{count}</h1><button onClick={handleClick}>+1</button></div>)
}
3.2 基本使用
-
使用場景:當你想要在函數組件中,處理副作用(side effect)時就要使用 useEffect Hook 了
-
作用:處理函數組件中的一些副作用(side effect)
-
注意:在實際開發中,副作用是不可避免的。因此,react 專門提供了 useEffect Hook 來處理函數組件中的副作用
語法:
- 參數:回調函數(稱為 effect),就是在該函數中寫副作用代碼
- 執行時機:該 effect 會在組件第一次渲染以及每次組件更新后執行
- 相當于 componentDidMount + componentDidUpdate
核心代碼
import { useEffect } from 'react'const Counter = () => {const [count, setCount] = useState(0)useEffect(() => {document.title = `當前已點擊 ${count} 次`})return (<div><h1>計數器:{count}</h1><button onClick={() => setCount(count + 1)}>+1</button></div>)
}
3.3 依賴
內容:
-
問題:如果組件中有另外一個狀態,另一個狀態更新時,剛剛的 effect 回調也會執行
-
默認情況:只要狀態發生更新 useEffect 的 effect 回調就會執行
-
性能優化:跳過不必要的執行,只在 count 變化時,才執行相應的 effect
-
語法:
- 第二個參數:可選,也可以傳一個數組,數組中的元素可以成為依賴項(deps)
- 該示例中表示:只有當 count 改變時,才會重新執行該 effect
核心代碼
import { useEffect } from 'react'const Counter = () => {const [count, setCount] = useState(0)const [loading, setLoading] = useState(false)useEffect(() => {document.title = `當前已點擊 ${count} 次`}, [count])return (<div><h1>計數器:{count}</h1><button onClick={() => setCount(count + 1)}>+1</button><button onClick={() => setLoading(!loading)}>切換 loading</button></div>)
}
3.4 不要對依賴項撒謊
內容:
- useEffect 回調函數(effect)中用到的數據(比如,count)就是依賴數據,就應該出現在依賴項數組中
- 如果 useEffect 回調函數中用到了某個數據,但是,沒有出現在依賴項數組中,就會導致一些 Bug 出現!
- 所以,不要對 useEffect 的依賴撒謊
const App = () => {const [count, setCount] = useState(0)// 錯誤演示:useEffect(() => {document.title = '點擊了' + count + '次'}, [])return (<div><h1>計數器:{count}</h1><button onClick={() => setCount(count + 1)}>+1</button></div>)
}
useEffect完全指南:https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
3.5 依賴項可以是空數組
內容:
-
useEffect 的第二個參數,還可以是一個空數組([]),表示只在組件第一次渲染后執行 effect
-
使用場景:1 事件綁定 2 發送請求獲取數據 等
-
語法:
- 該 effect 只會在組件第一次渲染后執行,因此,可以執行像事件綁定等只需要執行一次的操作
- 此時,相當于 class 組件的 componentDidMount 鉤子函數的作用
useEffect(() => {const handleResize = () => {}window.addEventListener('resize', handleResize)
}, [])
注意:
- 跟 useState Hook 一樣,一個組件中也可以調用 useEffect Hook 多次
- 推薦:一個 useEffect 只處理一個功能,有多個功能時,使用多次 useEffect
3.6 清理工作
內容:
- effect 的返回值是可選的,可省略。也可以返回一個清理函數,用來執行事件解綁等清理操作
- 清理函數的執行時機:
- 清理函數會在組件卸載時以及下一次副作用回調函數調用的時候執行,用于清除上一次的副作用。
- 如果依賴項為空數組,那么會在組件卸載時會執行。相當于組件的
componetWillUnmount
核心代碼:
useEffect(() => {const handleResize = () => {}window.addEventListener('resize', handleResize)// 這個返回的函數,會在該組件卸載時來執行// 因此,可以去執行一些清理操作,比如,解綁 window 的事件、清理定時器 等return () => window.removeEventListener('resize', handleResize)
}, [])
3.7 useEffect的 4 種使用使用方式
// 1
// 觸發時機:1 第一次渲染會執行 2 每次組件重新渲染都會再次執行
// componentDidMount + ComponentDidUpdate
useEffect(() => {})// 2(使用頻率最高)
// 觸發時機:只在組件第一次渲染時執行
// componentDidMount
useEffect(() => {}, [])// 3(使用頻率最高)
// 觸發時機:1 第一次渲染會執行 2 當 count 變化時會再次執行
// componentDidMount + componentDidUpdate(判斷 count 有沒有改變)
useEffect(() => {}, [count])// 4
useEffect(() => {// 返回值函數的執行時機:組件卸載時// 在返回的函數中,清理工作return () => {// 相當于 componentWillUnmount}
}, [])useEffect(() => {// 返回值函數的執行時機:1 組件卸載時 2 count 變化時// 在返回的函數中,清理工作return () => {}
}, [count])
3.8 發送請求
內容:
-
在組件中,可以使用 useEffect Hook 來發送請求(side effect)獲取數據
-
注意:effect 只能是一個同步函數,不能使用 async
- 因為如果 effect 是 async 的,此時返回值是 Promise 對象。這樣的話,就無法保證清理函數被立即調用
-
為了使用 async/await 語法,可以在 effect 內部創建 async 函數,并調用
核心代碼:
// 錯誤演示:不要給 effect 添加 async
useEffect(async () => {const res = await axios.get('http://xxx')return () => {}
}, [])// 正確使用
useEffect(() => {const loadData = async () => {const res = await axios.get('http://xxx')}loadData()return () => {}
}, [])
3.9 axios請求本地json數據
需求:發起axios請求本地某json數據
第一步:需要將json文件放在 public 目錄下 !!!
第二步:引入axios import axios from 'axios'
第三步:發起請求
import React, { useEffect, useState } from 'react'
import axios from 'axios'
export default function App() {const [list, setList] = useState([])useEffect(() => {
//接口地址可省略 public
axios.get('http://localhost:3000/data.json').then((res) => {console.log('打印res', res.data)setList(res.data.list)})}, [])return (<ul>{list.map((item) => (<li key={item.id}>{item.comment}</li>))}</ul>)
}