React-Hook快速入門(一)

一、React介紹

溫馨提醒:想要獲取更好的觀看效果,可以點(diǎn)擊查看本篇文章的原文檔(React-Hook快速入門(一) (notion.so)
react是基本的頁面渲染庫,基于不同的平臺有

  • react-dom: 瀏覽器
  • react-native: app環(huán)境
  • react-vr: vr平臺

二、為什么要使用React-Hook

在React-Hook誕生之前,React通常使用class作為組件。而function只能作為受控組件,它本身是沒有自己的狀態(tài)(state),只能通過接受props來被動渲染。引入React-Hook后,函數(shù)組件可以通過useState來擁有自己的狀態(tài);useEffect整合了各類生命周期,使得代碼邏輯更清晰;自定義hook使得代碼更容易復(fù)用。以下是官網(wǎng)對于React-Hook的介紹。

Hook 簡介 - React

簡而言之,hook主要解決了以下的一些問題:

  • 大型組件很難拆分和重構(gòu),也很難測試。
  • 業(yè)務(wù)邏輯分散在組件的各個方法之中,導(dǎo)致重復(fù)邏輯或關(guān)聯(lián)邏輯。
  • 組件類引入了復(fù)雜的編程模式,比如 render props 和高階組件。

核心思想:組件的最佳寫法應(yīng)該是函數(shù),而不是類。React Hooks 的意思是,組件盡量寫成純函數(shù),如果需要外部功能和副作用,就用鉤子把外部代碼"鉤"進(jìn)來。

三、useState

基本用法

const [state, setState] = useState(initValue)

useState使React函數(shù)組件擁有了狀態(tài)。

  • 括號里的initValue是state的初始值。
  • 數(shù)組解構(gòu)的第一個參數(shù)是最新的state值,每次state的值得改變將觸發(fā)頁面重新渲染。
  • 數(shù)組解構(gòu)的第二個參數(shù)是state的更新函數(shù),通過給setState(newState)傳遞參數(shù)newState來改變狀態(tài)值(state),并引發(fā)頁面的重新渲染。
import React, { useState } from 'react';

const Example = () => {
  // 聲明一個叫 "count" 的 state 變量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

setState是同步還是異步?

[圖片上傳失敗...(image-8c34b3-1621553852262)]

看下面一個例子

https://codesandbox.io/s/agitated-flower-9lhi2?file=/src/App.tsx:0-570

在執(zhí)行增加num(state值)的前后打印num,發(fā)現(xiàn)打印的結(jié)果均是0而不是最新值1,似乎setState是一個異步操作?

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [num, setNum] = useState(0);
  const addNum = () => {
    console.log("執(zhí)行setNum之前", num);
    setNum(num + 1)
    console.log("執(zhí)行setNum之后", num);
  };
  return (
    <div className="App">
      <h1>setState是同步還是異步的?</h1>
      <p>數(shù)字:{num}</p>
      <button onClick={addNum}>增加</button>
    </div>
  );
}

接著將代碼更改如下,在點(diǎn)擊增加按鈕的回調(diào)函數(shù)中一次性調(diào)用兩次setNum,結(jié)果發(fā)現(xiàn)頁面上顯示的數(shù)字是1而不是2。這是什么情況呢?

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [num, setNum] = useState(0);
  const addNum = () => {
    console.log("執(zhí)行setNum之前", num);
    setNum(num + 1)
    console.log("執(zhí)行setNum之后", num);
  };
  return (
    <div className="App">
      <h1>setState是同步還是異步的?</h1>
      <p>數(shù)字:{num}</p>
      <button onClick={addNum}>增加</button>
    </div>
  );
}

這是因?yàn)樵趫?zhí)行了setNum函數(shù)后,React并不會立馬去更新num。這樣在連續(xù)第二次調(diào)用setState時,num值仍然為0,因此連續(xù)調(diào)用兩次setNum后最新結(jié)果是1。React這樣做的目的是,連續(xù)多次的調(diào)用setState會合并state,而不是立馬去更新state,這樣就不會導(dǎo)致頁面在短暫時間進(jìn)行多次渲染,從而節(jié)省了頁面開銷。

那么我想連續(xù)調(diào)用兩次,而且最終結(jié)果顯示為2,該怎么實(shí)現(xiàn)?這個時候可以使用函數(shù)式更新

函數(shù)式更新

通過在useState傳入一個函數(shù),該函數(shù)的參數(shù)state即為更新之前的state值,通過在這個函數(shù)中執(zhí)行兩次num + 1的操作,最后返回的state將會比先前值+2,這樣保證了狀態(tài)的一致性,也驗(yàn)證了setState本身是一個同步函數(shù),只是它的狀態(tài)更新機(jī)制像一個異步函數(shù)!

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [num, setNum] = useState(0);
  const addNum = () => {
    console.log("執(zhí)行setNum之前", num);
    setNum((state) => {
      state += 1;
      state += 1
      console.log("執(zhí)行setNum的時候", state);
      return state;
    });
    console.log("執(zhí)行setNum之后", num);
  };
  return (
    <div className="App">
      <h1>setState是同步還是異步的?</h1>
      <p>數(shù)字:{num}</p>
      <button onClick={addNum}>增加</button>
    </div>
  );
}

惰性初始化

useState可以存字符串、數(shù)值等基本類型,也可以存數(shù)組、字符串等引用類型。那么可不可以存函數(shù)呢?

看下面一個例子

https://codesandbox.io/s/usestateduoxingchushihua-jthil?file=/src/App.tsx

通過callback存入一個函數(shù),在點(diǎn)擊更改函數(shù)按鈕時,變更存儲的函數(shù);在點(diǎn)擊執(zhí)行函數(shù)按鈕時,調(diào)用存入的函數(shù)。然而結(jié)果并不是像我們期望的那樣。在初始化state和點(diǎn)擊更改函數(shù)按鈕時,都自動執(zhí)行了存入的函數(shù)!原因是React的useState有著惰性初始化的特性

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [callback, setCallback] = useState(() => {alert('init')})
  return (
    <div className="App">
      <h1>useState惰性初始化</h1>
      <button onClick={() => {setCallback(() => {alert('change')})}}>更改函數(shù)</button>
      <button onClick={callback}>執(zhí)行函數(shù)</button>
    </div>
  );
}

Hook API 索引 - React

傳入函數(shù)給useState,React并不會認(rèn)為你要存的是一個函數(shù)。相反他會認(rèn)為這是一個非常消耗性能的計(jì)算state的操作,他會立即去執(zhí)行還函數(shù),并將該函數(shù)的返回值作為新的state。

那么useState如何存一個函數(shù)呢?

其實(shí)很簡單,只要將上述代碼修改如下即可。

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [callback, setCallback] = useState(() => () => {alert('init')})
  return (
    <div className="App">
      <h1>useState惰性初始化</h1>
      <button onClick={() => {setCallback(() => () => {alert('change')})}}>更改函數(shù)</button>
      <button onClick={callback}>執(zhí)行函數(shù)</button>
    </div>
  );
}

四、useEffect

基本用法

useEffect(() => {
  // do some effect function
}, [dependence]);

在DOM更新完畢之后執(zhí)行副作用函數(shù),可以取代class的生命周期函數(shù)。

  • 當(dāng)沒有依賴項(xiàng),會在組件每次更新后執(zhí)行
  • 依賴項(xiàng)為空數(shù)組:會在組件掛載和卸載時執(zhí)行
  • 依賴項(xiàng)為變量時,會在這些變量改變后才執(zhí)行

清除effect

通常,組件卸載時需要清除 effect 創(chuàng)建的諸如訂閱或計(jì)時器 ID 等資源。要實(shí)現(xiàn)這一點(diǎn),useEffect 函數(shù)需返回一個清除函數(shù)。

看下面一個例子

https://codesandbox.io/s/wispy-frog-bb3wp?file=/src/App.tsx

import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";

export default function Test() {
  const [num, setNum] = useState<number>(0);
  useEffect(() => {
    console.log("在return之后執(zhí)行");
    return () => {
      console.log("return先執(zhí)行");
    };
  }, [num]);
  return (
    <div>
      <h2>Test測試頁面</h2>
      <Link to="/other">前往Other</Link>
      <p>數(shù)字:{num}</p>
      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        增加
      </button>
    </div>
  );
}

  • React會在第一次渲染時執(zhí)行useEffect中的函數(shù),韓是不會執(zhí)行return。
  • effect 在之后的每次渲染的時候都會執(zhí)行。此時先執(zhí)行return函數(shù),再執(zhí)行effect中的副作用。
  • React 會在組件卸載的時候執(zhí)行清除操作(即執(zhí)行return)

依賴項(xiàng)的注意事項(xiàng)

一般選擇什么作為useEffect的依賴項(xiàng)?選擇依賴項(xiàng)時需要注意些什么呢?

看下面一個例子

https://codesandbox.io/s/trusting-cherry-7i2wt?file=/src/App.tsx

分別選擇常量、狀態(tài)對象和普通對象作為依賴項(xiàng)??梢园l(fā)現(xiàn),選擇常量時,除了第一次渲染以外,之后每次頁面渲染都不會去調(diào)用useEffect;選擇狀態(tài)對象時,每次該狀態(tài)改變時(即頁面重新渲染后),都會去調(diào)用useEffect;而選擇一般對象時,會陷入無限循環(huán)的去調(diào)用useEffect。

import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [num, setNum] = useState(0);
  const [text, setText] = useState<string[]>([]);
  const constValue = "小花";
  const object = { name: "小花" };

  useEffect(() => {
    console.log("頁面重新渲染了");
    setText(["小花", "小明", "囂張"]);
  }, [constValue]);
  return (
    <div className="App">
      <h1>選擇依賴項(xiàng)的注意事項(xiàng)</h1>
      <p>數(shù)字:{num}</p>
      <button
        onClick={() => {
          setNum(num + 1);
        }}
      >
        增加
      </button>
      {text.map((item) => (
        <div>{item}</div>
      ))}
    </div>
  );
}

  • 絕不可以使用非狀態(tài)的對象作為依賴,因?yàn)槊看谓M件更新后,該對象的地址都會發(fā)生改變,最終導(dǎo)致不停地的調(diào)用useEffect。
  • 可以使用常量和狀態(tài)對象作為依賴,因?yàn)闋顟B(tài)對象在組建更新后并不會改變,除非調(diào)用setState改變。

五、自定義hook

Hook 使用規(guī)則

  • 只能在函數(shù)最外層調(diào)用 Hook。不要在循環(huán)、條件判斷或者子函數(shù)中調(diào)用。
  • 只能在 React 的函數(shù)組件中調(diào)用 Hook。不要在其他 JavaScript 函數(shù)中調(diào)用。(還有一個地方可以調(diào)用 Hook —— 就是自定義的 Hook 中,我們稍后會學(xué)習(xí)到。)

為什么只能在函數(shù)最外層調(diào)用hook?

Hook 規(guī)則 - React

自定義一個useArray hook

遵循React的規(guī)則,使用React提供的基礎(chǔ)hook,自定義一個hook。該hook封裝了對數(shù)組的操作,在其他地方可以方便的調(diào)用這個hook來完成對數(shù)組的操作

點(diǎn)擊查看useArray的實(shí)現(xiàn)

https://codesandbox.io/s/fervent-hodgkin-g0ipv?file=/src/App.tsx

注意事項(xiàng)

  • 自定義hook一定要使用use開頭的命名規(guī)范,否則會直接報(bào)錯
  • 不要在回調(diào)函數(shù)中調(diào)用hook,應(yīng)該把需要用到的回調(diào)方法通過hook返回,然后在需要用到的函數(shù)組件最外層獲取該方法,這樣就可以在回調(diào)函數(shù)中調(diào)用該方法。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容