React Hooks 學習筆記

      網友投稿 881 2022-05-30

      文章出處: 拉 勾 大前端 高薪訓練營

      1. React Hooks 介紹

      對函數型組件進行增強,讓函數型組件可以存儲狀態,可以擁有處理副作用的能力,讓開發者在不使用類組件的情況下,實現相同的功能。

      缺少邏輯復用的機制

      為了復用邏輯增加無實際渲染效果的組件,增加了組件層級,顯示十分臃腫,增加了調試的難度以及運行效率的降低

      類組件經常會變得很復雜難以維護

      將一組相干的業務邏輯拆分到了多個生命周期函數中,在一個生命周期函數內,存在多個不相干的業務邏輯

      類成員方法不能保證 this 指向的正確性

      2. React Hooks 使用

      Hooks 意為鉤子, React Hooks 就是一堆鉤子函數, React 通過這些鉤子函數對函數型組件進行增強,不同的鉤子函數提供了不同的功能

      用于為函數組件引入狀態

      import {useState} from 'react' function App() { const [count, setCount] = useState(0) return (

      {count}
      ); } export default App;

      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 (

      {count}
      ) }

      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 ( ) } function Foo () { const value = useContext(countContext) return (

      I am Foo {value}
      ) }

      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 (

      {count}
      ) }

      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 (

      {count}
      ) }

      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 (

      {count}
      ) }

      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 (

      {count} {result} {bool ? '真' : '假'}
      ) }

      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

      I am Foo
      })

      1

      2

      3

      4

      5

      性能優化,緩存函數,使用組件重新渲染時得到相同的函數實例。否則每次父組件渲染,函數變量的實例都會變化,導致里層組件被重新渲染

      import { useState, memo, useCallback } from "react"; const Foo = memo(function Foo (props) { console.log('Foo 重新渲染了') return

      I am Foo
      }) export default function App () { const [count, setCount] = useState(0) const resetCount = useCallback(() => { setCount(0) }, [setCount]) return (
      {count}
      ) }

      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 (

      {count}
      ) }

      1

      2

      3

      4

      5

      6

      React Hooks 學習筆記

      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}

      {post.body}
      ) }

      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( , document.getElementById('root') );

      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( , document.getElementById('root') ) } export default function App () { const [count, setCount] = useState(0) const [name, setName] = useState('張三') return

      {count} {name}
      }

      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( , document.getElementById('root') ) } // 上一次的依賴值 let prevDepsAry = [] let effectIndex = 0 /** * useEffect * @param {function} callback 回調函數 * @param {Array} depsAry 依賴數組 * @returns {function} 清理函數 */ function useEffect (callback, depsAry) { if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一個參數必須是一個函數') if (typeof depsAry === 'undefined') { // 沒有傳遞 callback() } else { // 判斷 depsAry 是不是數組 if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二個參數必須是一個數組') // 獲取上一次的狀態值 let prevDeps = prevDepsAry[effectIndex] // 將當前的依賴值和上一次的依賴值作對比,如果有變化,調用 callback let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true // 判斷值是否有變化 if (hasChanged) { callback() } // 同步依賴值 prevDepsAry[effectIndex++] = depsAry } } export default function App () { const [count, setCount] = useState(0) const [name, setName] = useState('張三') useEffect(() => { console.log('Hello') }, [count]) useEffect(() => { console.log('World') }, [name]) // 測試不傳監聽數據的情況 useEffect(() => { console.log('xxx') }, []) return

      {count} {name}
      }

      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( , document.getElementById('root') ) } // 上一次的依賴值 let prevDepsAry = [] let effectIndex = 0 /** * useEffect * @param {function} callback 回調函數 * @param {Array} depsAry 依賴數組 * @returns {function} 清理函數 */ function useEffect (callback, depsAry) { if (Object.prototype.toString.call(callback) !== '[object Function]') throw new Error('useEffect 第一個參數必須是一個函數') if (typeof depsAry === 'undefined') { // 沒有傳遞 callback() } else { // 判斷 depsAry 是不是數組 if (Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error('useEffect 第二個參數必須是一個數組') // 獲取上一次的狀態值 let prevDeps = prevDepsAry[effectIndex] // 將當前的依賴值和上一次的依賴值作對比,如果有變化,調用 callback let hasChanged = prevDeps ? !depsAry.every((dep, index) => dep === prevDeps[index]) : true // 判斷值是否有變化 if (hasChanged) { callback() } // 同步依賴值 prevDepsAry[effectIndex++] = depsAry } } function useReducer (reducer, initialState) { const [state, setState] = useState(initialState) function dispatch (action) { const newState = reducer(state, action) setState(newState) } return [state, dispatch] } 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 [cnt, dispatch] = useReducer(reducer, 0) return

      {cnt}
      }

      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小時內刪除侵權內容。

      上一篇:Python 網絡編程之網絡基礎
      下一篇:一文了解各大圖數據庫查詢語言(Gremlin vs Cypher vs nGQL)| 操作入門篇
      相關文章
      亚洲爆乳无码一区二区三区| 亚洲嫩模在线观看| 亚洲日韩中文字幕天堂不卡 | 亚洲开心婷婷中文字幕| www.亚洲精品.com| 国产精品亚洲专区一区| 国产成人高清亚洲一区91| 国产偷国产偷亚洲高清人| 另类小说亚洲色图| 亚洲精品视频久久久| 色噜噜AV亚洲色一区二区| 亚洲综合av永久无码精品一区二区 | 亚洲精品无码久久久久A片苍井空| 中文字幕亚洲男人的天堂网络| 自拍日韩亚洲一区在线| 亚洲无码一区二区三区| 亚洲国产精品成人综合色在线| 亚洲AV成人精品日韩一区| 国产亚洲精品免费| 亚洲日韩中文字幕日韩在线| 国产亚洲精午夜久久久久久| 亚洲精品亚洲人成人网| 亚洲日本在线看片| 亚洲欧洲日韩国产| 77777午夜亚洲| 亚洲第一综合天堂另类专| 337p日本欧洲亚洲大胆人人| 亚洲国产成人精品女人久久久 | 亚洲经典在线中文字幕| 亚洲人成在线免费观看| 伊人久久五月丁香综合中文亚洲| 亚洲欧美日韩国产成人| 亚洲AV成人潮喷综合网| 亚洲无av在线中文字幕| 亚洲人成网www| 亚洲AV男人的天堂在线观看| 国产精品无码亚洲精品2021| 国产偷窥女洗浴在线观看亚洲| 人人狠狠综合久久亚洲婷婷| 亚洲视频小说图片| 亚洲综合无码一区二区痴汉 |