React Hooks 學習筆記
文章出處: 拉 勾 大前端 高薪訓練營
1. React Hooks 介紹
對函數型組件進行增強,讓函數型組件可以存儲狀態,可以擁有處理副作用的能力,讓開發者在不使用類組件的情況下,實現相同的功能。
缺少邏輯復用的機制
為了復用邏輯增加無實際渲染效果的組件,增加了組件層級,顯示十分臃腫,增加了調試的難度以及運行效率的降低
類組件經常會變得很復雜難以維護
將一組相干的業務邏輯拆分到了多個生命周期函數中,在一個生命周期函數內,存在多個不相干的業務邏輯
類成員方法不能保證 this 指向的正確性
2. React Hooks 使用
Hooks 意為鉤子, React Hooks 就是一堆鉤子函數, React 通過這些鉤子函數對函數型組件進行增強,不同的鉤子函數提供了不同的功能
用于為函數組件引入狀態
import {useState} from 'react' function App() { const [count, setCount] = useState(0) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
接受唯一的參數即狀態初始值,初始值可以是任意數據類型。
const [count, setCount] = useState(0) const [person, setPerson] = useState({ name: '張三', age: 20 })
1
2
返回值為數組,數組中存儲值和更改狀態值的方法,方法名稱約定以 set 開頭,后面加上狀態名稱。
const [count, setCount] = useState(0)
1
方法可以被調用多次,用以保存不同狀態值
1
2
3
參數可以是一個函數,函數返回什么,初始狀態值就是什么,函數只會被調用一次,在初始值是動態值的情況。
// 當初始值是動態值 // 這樣寫每次渲染都會執行 const propsCount = props.count || 0 // const [count, setCount] = useState(propsCount) // 應該這樣寫 const [count, setCount] = useState(() => { return props.count || 0 // 只有第一次執行的時候才會執行 })
1
2
3
4
5
6
7
8
9
設置狀態值方法的參數可以是一個值也可以是一個函數,設置狀態值方法的方法本身是異步的
如果代碼依賴狀態值,那要寫在回調函數中:
function handleCount () { setCount((count) => { const newCount = count + 1 document.title = newCount return newCount }) }
1
2
3
4
5
6
7
8
9
useReducer 是另一種讓函數組件保存狀態的方式,可以將 dispatch 傳給子組件使用
import { useReducer } from "react"; export default function App () { function reducer (state, action) { switch (action.type) { case 'increment': return state + 1 case 'decrement': return state - 1 default: return state; } } const [count, dispatch] = useReducer(reducer, 0) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在跨組件層級獲取數據時簡化獲取數據的代碼
import { createContext, useContext } from "react"; const countContext = createContext() export default function App () { return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
讓函數型組件擁有處理副作用的能力,類似生命周期函數
useEffect 執行機制
可以把 useEffect 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 這三個函數的組合
useEffect(() => {}) => componentDidMount, componentDidUpdate
useEffect(() => {}, []) => componentDidMount
useEffect(() => () => {}) => componentDidUpdate, componentWillUnmount
useEffect(() => () => {}, []) => componentWillUnmount
import { useEffect, useState } from "react"; import ReactDOM from 'react-dom' export default function App () { const [count, setCount] = useState(0) // 組件掛載完成之后執行,組件數據更新之后執行 // useEffect(() => { // console.log('123') // }) // 組件掛載完成之后執行 // useEffect(() => { // console.log('456') // }, []) useEffect(() => { return () => { console.log('組件被卸載了') } }) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
useEffect 使用方法
(1). 為 window 對象添加滾動事件
(2). 設置定時器讓 count 數值每隔一秒增加 1
import { useEffect, useState } from "react"; import ReactDOM from 'react-dom' export default function App () { const [count, setCount] = useState(0) function onScroll () { console.log('頁面滾動了') } useEffect(() => { window.addEventListener('scroll', onScroll) return () => { window.removeEventListener('scroll', onScroll) } }, []) useEffect(() => { const timerId = setInterval(() => { setCount(count => { const newCount = count + 1 document.title = newCount return newCount }) }, 1000); return () => { clearTimeout(timerId) } }, []) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
useEffect 解決的問題
(1). 按照用途將代碼進行分類(將一組相同的業務邏輯歸置到了同一個副作用函數中)
(2). 簡化重復代碼,是組件內部代碼更加清晰
只有指定數據發生變化時觸發 effect
import { useEffect, useState } from "react"; export default function App () { const [count, setCount] = useState(0) const [person, setPerson] = useState({name: '張三'}) useEffect(() => { // person 的變化不會觸發 useEffect , 因為第二個數組參數中只監聽了 count document.title = count console.log(111) }, [count]) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
useEffect 鉤子函數結合異步函數
useEffect 中的參數函數不能是異步函數,因為 useEffect 函數要返回清理資源的函數,如果是異步函數就變成了返回 Promise. 可以寫成自執行函數的形式:
useEffect(() => { (async () => { await axios.get() })() }, [])
1
2
3
4
5
useMemo 的行為類似 Vue 中的計算屬性,可以檢測某個值的變化,根據變化只計算新值。
useMemo 會緩存計算結果,如果檢測子沒有發生變化,及時組建重新渲染,也不會重新計算,此行為可以有助于避免在每個渲染上進行昂貴的計算。
import { useState, useMemo } from "react"; export default function App () { const [count, setCount] = useState(0) const [bool, setBool] = useState(true) const result = useMemo(() => { console.log('111') // 只有 count 改變才會重新執行這個回調函數 return count * 2 }, [count]) return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
性能優化,如果本組件中的數據沒有發生變化,阻止組件更新,類似類組件中的 PureComponent 和 shouldComponentUpdate
import { memo } from 'react' const Foo = memo(function Foo () { return
1
2
3
4
5
性能優化,緩存函數,使用組件重新渲染時得到相同的函數實例。否則每次父組件渲染,函數變量的實例都會變化,導致里層組件被重新渲染
import { useState, memo, useCallback } from "react"; const Foo = memo(function Foo (props) { console.log('Foo 重新渲染了') return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { useRef } from "react"; export default function App () { const box = useRef() return (
1
2
3
4
5
6
7
8
9
10
即使組件重新渲染,保存的數據仍然還在,保存的數據被更改不會觸發組件重新渲染。
import { useRef, useState, useEffect} from "react"; export default function App () { const [count, setCount] = useState(0) let timeId = useRef() // 夸組件生命周期 useEffect(() => { // 使用這個 ref 的 current 屬性存儲數據 timeId.current = setInterval(() => { setCount(count => count + 1) }, 1000); }, []) const stopCount = () => { console.log(timeId.current) clearInterval(timeId.current) } const box = useRef() return (
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
3. 自定義 Hook (為了在組件之間形成邏輯共享)
自定義 Hook 是標準的封裝和共享邏輯的方式
自定義 Hook 是一個函數,其名稱以 use 開頭
自定義 Hook 其實就是邏輯和內置 Hook 的組合
如何使用自定義 Hook:
import { useState, useEffect} from "react"; import axios from "axios"; function useGetPost () { const [post, setPost] = useState({}) useEffect(() => { axios.get('https://jsonplaceholder.typicode.com/posts/1') .then((response) => { setPost(response.data) }) }, []) return [post, setPost] } export default function App () { const [post, setPost] = useGetPost() return (
{post.title}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
封裝公共邏輯:
import { useState} from "react"; function useUpdateInput (initialState) { const [value, setValue] = useState(initialState) return { value, onChange: e => setValue(e.target.value) } } export default function App () { const usernameInput = useUpdateInput('') const passwordInput = useUpdateInput('') const submitForm = event => { event.preventDefault(); console.log(usernameInput.value) console.log(passwordInput.value) } return (
) }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
4. React 路由 Hooks
index.js
import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router } from "react-router-dom"; import App from './App'; ReactDOM.render(
1
2
3
4
5
6
7
8
9
10
11
App.js
import { Link, Route } from "react-router-dom"; import Home from './pages/Home' import List from './pages/List' export default function App () { return ( <>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Home.js
import { useHistory, useLocation, useRouteMatch, useParams } from "react-router-dom"; export default function Home(props) { console.log(props) console.log(useHistory()) console.log(useLocation()) console.log(useRouteMatch()) console.log(useParams()) return
Home works
1
2
3
4
5
6
7
8
9
10
11
12
輸出結果:
{history: {…}, location: {…}, match: {…}, staticContext: undefined}
{length: 7, action: “PUSH”, location: {…}, createHref: ?, push: ?, …}
{pathname: “/home/zhangsan”, search: “”, hash: “”, state: undefined, key: “o6w5y3”}
{path: “/home/:name”, url: “/home/zhangsan”, isExact: true, params: {…}}
{name: “zhangsan”}
List.js
export default function List(props) { console.log(props) return
List works
1
2
3
4
5
6
使用數組 state 存儲狀態,用數組 setters 存儲 setState方法,利用閉包管理,將 下標 stateIndex 緩存在閉包中,創建 對應的 setState.
// import { useState } from 'react' import ReactDOM from 'react-dom' let state = [] let setters = [] let stateIndex = 0 function createSetter (index) { return function (newState) { state[index] = newState render() } } function useState (initialState) { state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState setters.push(createSetter(stateIndex)) let value = state[stateIndex] let setter = setters[stateIndex] stateIndex ++ return [value, setter] } function render () { stateIndex = 0 ReactDOM.render(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// import { useState } from 'react' import ReactDOM from 'react-dom' let state = [] let setters = [] let stateIndex = 0 function createSetter (index) { return function (newState) { state[index] = newState render() } } function useState (initialState) { state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState setters.push(createSetter(stateIndex)) let value = state[stateIndex] let setter = setters[stateIndex] stateIndex ++ return [value, setter] } function render () { stateIndex = 0 effectIndex = 0 ReactDOM.render(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// import { useState } from 'react' // import { useReducer } from 'react' import ReactDOM from 'react-dom' let state = [] let setters = [] let stateIndex = 0 function createSetter (index) { return function (newState) { state[index] = newState render() } } function useState (initialState) { state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState setters.push(createSetter(stateIndex)) let value = state[stateIndex] let setter = setters[stateIndex] stateIndex ++ return [value, setter] } function render () { stateIndex = 0 effectIndex = 0 ReactDOM.render(
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
React 數據結構
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。