欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文教你如何避免React中常見的8個(gè)錯(cuò)誤

 更新時(shí)間:2023年12月21日 08:34:53   作者:CUGGZ  
這篇文章主要來(lái)和大家一起分享在?React?開發(fā)中常見的一些錯(cuò)誤,以及如何避免這些錯(cuò)誤,理解這些問(wèn)題背后的細(xì)節(jié),防止犯下類似的錯(cuò)誤,需要的可以參考下

1. 組件卸載后執(zhí)行狀態(tài)更新

報(bào)錯(cuò)信息: Can’t perform a React state update on an unmounted component

這個(gè)報(bào)錯(cuò)就是因?yàn)樵诮M件樹的某個(gè)地方,狀態(tài)更新被觸發(fā)到已經(jīng)卸載的組件上了。也就是說(shuō),我們不能在組件銷毀后設(shè)置 state,防止出現(xiàn)內(nèi)存泄漏。

const Component = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchAsyncData().then((data) => setData(data));
  }, []);

  // ...
};

比如,在請(qǐng)求數(shù)據(jù)時(shí),由于跳轉(zhuǎn)到了B頁(yè)面,A頁(yè)面的數(shù)據(jù)請(qǐng)求還在進(jìn)行中,但是頁(yè)面已經(jīng)銷毀了,就會(huì)出現(xiàn)這種情況。那該如何解決這個(gè)問(wèn)題呢?有兩種方法:

(1)組件卸載時(shí)取消異步請(qǐng)求

第一種方法(推薦),就是在組件卸載時(shí)取消異步請(qǐng)求。一些異步請(qǐng)求庫(kù)提供了取消異步請(qǐng)求的方法。如果沒(méi)有使用第三方庫(kù),可以使用 AbortController 來(lái)取消。這種方法本質(zhì)上就是在組件卸載時(shí)取消副作用:

const Component = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    fetch(url, { signal: controller.signal }).then((data) => setData(data));
    return () => {
      controller.abort();
    }
  }, []);

  // ...
};

(2)跟蹤組件是否已掛載

另外,可以跟蹤組件的掛載狀態(tài),如果還沒(méi)掛載或已經(jīng)卸載,返回 false;否則返回 true:

const Component = () => {
  const [data, setData] = useState(null);
  const isMounted = useRef(true);

  useEffect(() => {
    fetchAsyncData().then(data => {
      if(isMounted.current) {
        setData(data);
      }
    });

    return () => {
      isMounted.current = false;
    };
  }, []);

  // ...
}

不過(guò),不建議使用這種方法。這樣保留了未掛載組件的引用,可能會(huì)導(dǎo)致內(nèi)存泄漏和性能問(wèn)題。

2. 渲染列表時(shí)不使用 key

報(bào)錯(cuò)信息: Warning: Each child in a list should have a unique key prop

React 開發(fā)中最常見的就是遍歷數(shù)組來(lái)渲染組件。在JSX中,可以使用Array.map將該邏輯嵌入到組件中,并在回調(diào)中返回所需的組件。如下:

import { Card } from "./Card";

const data = [
  { id: 1, text: "JavaScript" },
  { id: 2, text: "TypeScript" },
  { id: 3, text: "React" }
];

export default function App() {
  return (
    <div className="container">
      {data.map((content) => (
        <div className="card">
          <Card text={content.text} />
        </div>
      ))}
    </div>
  );
}

這樣會(huì)收到如下警告:Warning: Each child in a list should have a unique key prop,這表示需要給生成的每個(gè)組件一個(gè)唯一的key。所以,要在map回調(diào)返回的JSX的最外層元素添加一個(gè)key值,該值應(yīng)該是一個(gè)字符串或者數(shù)字,并且在這個(gè)組件列表中應(yīng)該是唯一的。

export default function App() {
  return (
    <div className="container">
      {data.map((content) => (
        <div key={content.id} className="card">
          <Card text={content.text} />
        </div>
      ))}
    </div>
  );
}

盡管不遵守這個(gè)要求也不會(huì)導(dǎo)致應(yīng)用崩潰,但它可能會(huì)導(dǎo)致一些意外的情況。React 會(huì)使用這些key來(lái)確定列表中的哪些子項(xiàng)發(fā)生了更改,并使用此信息來(lái)確定可以重用先前 DOM 的哪些部分,以及在重新渲染組件時(shí)應(yīng)該重新計(jì)算哪些部分。 因此,建議添加 key。

3. Hooks 調(diào)用順序錯(cuò)誤

報(bào)錯(cuò)信息: React Hook "useXXX" is called conditionally. React Hooks must be called in the exact same order in every component render

先來(lái)看下面的代碼:

const Toggle = () => {
  const [isOpen, setIsOpen] = useState(false);

  if (isOpen) {
    return <div>{/* ... */}</div>;
  }
  const openToggle = useCallback(() => setIsOpen(true), []);
  return <button onClick={openToggle}>{/* ... */}</button>;
};

當(dāng) isOpen 的值為true時(shí),就會(huì)直接return那個(gè)div元素。這樣當(dāng)isOpen的值為truefalse時(shí)useCallback Hook的調(diào)用順序就不一致了。這時(shí)React就會(huì)警告我們:React Hook "useCallback" is called conditionally. React Hooks must be called in the exact same order in every component render。這其實(shí)就是React官方文檔中所說(shuō)的,不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook, 確保總是在 React 函數(shù)的最頂層以及任何 **return** 之前調(diào)用他們。遵守這條規(guī)則才能確保 Hook 在每一次渲染中都按照同樣的順序被調(diào)用。

可以這樣來(lái)修改上面的代碼:

const Toggle = () => {
  const [isOpen, setIsOpen] = useState(false);
  const openToggle = useCallback(() => setIsOpen(true), []);

  if (isOpen) {
    return <div>{/* ... */}</div>;
  }
  return <button onClick={openToggle}>{/* ... */}</button>;
};

4. useEffect 缺少依賴

報(bào)錯(cuò)信息: React Hook useEffect has a missing dependency: 'XXX'. Either include it or remove the dependency array

先來(lái)看看 React 官網(wǎng)給出的例子:

function Example({ someProp }) {
  function doSomething() {
    console.log(someProp);
  }

  useEffect(() => {
    doSomething();
  }, []); 
}

useEffect中定義空的依賴數(shù)組是不安全,因?yàn)樗{(diào)用的 doSomething 函數(shù)使用了 someProp。這時(shí)就會(huì)報(bào)錯(cuò):React Hook useEffect has a missing dependency: 'XXX'. Either include it or remove the dependency array。當(dāng)props中的someProp發(fā)生變化時(shí),函數(shù)doSomething的結(jié)果就會(huì)發(fā)生變化,然而useEffect的依賴數(shù)組為空,所以就不會(huì)執(zhí)行回調(diào)中的內(nèi)容。

有兩種方式來(lái)解決這個(gè)問(wèn)題:

useEffect中聲明其所需函數(shù),這種方式適用于只需要調(diào)用一次的函數(shù),比如初始化函數(shù):

function Example({ someProp }) {
  useEffect(() => {
    function doSomething() {
      console.log(someProp);
    }	
    doSomething();
  }, [someProp]); 
}

使用useCallback來(lái)定義依賴項(xiàng),確保當(dāng)自身依賴發(fā)生改變時(shí)函數(shù)主體也會(huì)改變:

function Example({ someProp }) {
  const doSomething = useCallback(() => {
    console.log(someProp);
  }, [someProp])

  useEffect(() => {
    doSomething();
  }, [doSomething]); 
}

5. 重新渲染過(guò)多

報(bào)錯(cuò)信息: Too many re-renders. React limits the number of renders to prevent an infinite loop

這個(gè)報(bào)錯(cuò)就是說(shuō)重新渲染過(guò)多。React 限制渲染的數(shù)量以防止無(wú)限循環(huán)。當(dāng)組件在很短的時(shí)間有太多狀態(tài)更新時(shí),就可能會(huì)發(fā)生這種情況。導(dǎo)致無(wú)限循環(huán)的最常見原因是:

  • 直接在渲染中執(zhí)行狀態(tài)更新;
  • 未向事件處理程序提供適當(dāng)?shù)幕卣{(diào)。

如果遇到這個(gè)警告,可以檢查組件的這兩個(gè)方面:

const Component = () => {
  const [count, setCount] = useState(0);

  setCount(count + 1); // 渲染中的狀態(tài)更新

  return (
    <div className="App">
      {/* onClick 沒(méi)有正確的回調(diào) */}
      <button onClick={setCount((prevCount) => prevCount + 1)}>
        Increment that counter
      </button>
    </div>
  );
}

6. 渲染的單條數(shù)據(jù)為對(duì)象

報(bào)錯(cuò)信息: Objects are not valid as a React child / Functions are not valid as a React child

在 React 中,我們可以在組件中渲染到 DOM 中的東西有很多,比如:HTML標(biāo)簽、JSX元素、原始 JavaScript 值、JavaScript 表達(dá)式等。但是不能將對(duì)象和函數(shù)渲染到 DOM 中,因?yàn)檫@兩個(gè)值不會(huì)解析為有意義的值,如果渲染了對(duì)象或函數(shù),就會(huì)報(bào)上面的錯(cuò)誤。解決這個(gè)問(wèn)題的方法很簡(jiǎn)單,就是檢查渲染的內(nèi)容是否是有效的值:

const Component = ({ body }) => (
  <div>
    <h1>{/* */}</h1>
    {/* 必須確保 body 是有效的 React child */}
    <div className="body">{body}</div>
  </div>
);

7. 相鄰JSX元素沒(méi)有包裝在封閉標(biāo)記中

報(bào)錯(cuò)信息: Adjacent JSX elements must be wrapped in an enclosing tag

這個(gè)報(bào)錯(cuò)就是說(shuō)相鄰JSX元素必須包裝在封閉標(biāo)記中,也就是必須要有一個(gè)根元素:

const Component = () => (
  <Nice />
  <Bad />
);

從 React 開發(fā)人員的角度來(lái)看,這個(gè)組件只會(huì)在另一個(gè)組件內(nèi)部使用。 因此,在他們的心智模型中,從一個(gè)組件返回兩個(gè)元素是有意義的,因?yàn)樯傻?DOM 結(jié)構(gòu)將是相同的,無(wú)論外部元素是在此組件中定義還是在父組件中定義。但是,React 無(wú)法做出這種假設(shè)。該組件可能會(huì)在根目錄中使用并破壞應(yīng)用,因?yàn)樗鼤?huì)導(dǎo)致無(wú)效的 DOM 結(jié)構(gòu)。

所以,應(yīng)該始終將組件返回的多個(gè) JSX 元素包裝在一個(gè)封閉標(biāo)記中??梢允且粋€(gè)元素、一個(gè)組件或者 React Fragment:

const Component = () => (
  <React.Fragment>
    <Nice />
  	<Bad />
  </React.Fragment>
);

或者直接使用一個(gè)空標(biāo)簽來(lái)包裝兩個(gè) JSX 元素:

const Component = () => (
  <>
    <Nice />
  	<Bad />
  </>
);

8. 使用舊的狀態(tài)

先來(lái)看一個(gè)計(jì)數(shù)器的例子:

const Increaser = () => {
  const [count, setCount] = useState(0);
  
  const increase = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  
  const handleClick = () => {
    increase();
    increase();
    increase();
  };
  
  return (
    <>
      <button onClick={handleClick}>+</button>
      <div>Counter: {count}</div>
    </>
  );
}

這里的handleClick方法會(huì)在點(diǎn)擊按鈕后執(zhí)行三次增加狀態(tài)變量count的操作。那么點(diǎn)擊一次是否會(huì)增加3呢?事實(shí)并非如此。點(diǎn)擊按鈕之后,count只會(huì)增加1。問(wèn)題就在于,當(dāng)我們點(diǎn)擊按鈕時(shí),相當(dāng)于下面的操作:

const handleClick = () => {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
};

當(dāng)?shù)谝淮握{(diào)用setCount(count + 1)時(shí)是沒(méi)有問(wèn)題的,它會(huì)將count更新為1。接下來(lái)第2、3次調(diào)用setCount時(shí),count還是使用了舊的狀態(tài)(count為0),所以也會(huì)計(jì)算出count為1。發(fā)生這種情況的原因就是狀態(tài)變量會(huì)在下一次渲染才更新。

解決這個(gè)問(wèn)題的辦法就是,使用函數(shù)的方式來(lái)更新狀態(tài):

const Increaser = () => {
  const [count, setCount] = useState(0);
  
  const increase = useCallback(() => {
    setCount(count => count + 1);
  }, [count]);
  
  const handleClick = () => {
    increase();
    increase();
    increase();
  };
  
  return (
    <>
      <button onClick={handleClick}>+</button>
      <div>Counter: {count}</div>
    </>
  );
}

這樣改完之后,React就能拿到最新的值,當(dāng)點(diǎn)擊按鈕時(shí),就會(huì)每次增加3。所以需要記?。?strong>如果要使用當(dāng)前狀態(tài)來(lái)計(jì)算下一個(gè)狀態(tài),就要使用函數(shù)的式方式來(lái)更新狀態(tài):

setValue(prevValue => prevValue + someResult)

以上就是一文教你如何避免React中常見的8個(gè)錯(cuò)誤的詳細(xì)內(nèi)容,更多關(guān)于React常見錯(cuò)誤避免的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ReactJS實(shí)現(xiàn)表單的單選多選和反選的示例

    ReactJS實(shí)現(xiàn)表單的單選多選和反選的示例

    本篇文章主要介紹了ReactJS實(shí)現(xiàn)表單的單選多選和反選的示例,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-10-10
  • React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例

    React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例

    這篇文章主要為大家介紹了React實(shí)現(xiàn)antdM的級(jí)聯(lián)菜單實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • React父組件調(diào)用子組件中的方法實(shí)例詳解

    React父組件調(diào)用子組件中的方法實(shí)例詳解

    最近做一個(gè)React項(xiàng)目,所有組件都使用了函數(shù)式組件,遇到一個(gè)父組件調(diào)用子組件方法的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于React父組件調(diào)用子組件中方法的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • React?Fiber?樹思想解決業(yè)務(wù)實(shí)際場(chǎng)景詳解

    React?Fiber?樹思想解決業(yè)務(wù)實(shí)際場(chǎng)景詳解

    這篇文章主要為大家介紹了React?Fiber?樹思想解決業(yè)務(wù)實(shí)際場(chǎng)景詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • React中的useEffect useLayoutEffect到底怎么用

    React中的useEffect useLayoutEffect到底怎么用

    這篇文章主要介紹了React中的useEffect useLayoutEffect具體使用方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • React性能優(yōu)化系列之減少props改變的實(shí)現(xiàn)方法

    React性能優(yōu)化系列之減少props改變的實(shí)現(xiàn)方法

    這篇文章主要介紹了React性能優(yōu)化系列之減少props改變的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • React Native仿美團(tuán)下拉菜單的實(shí)例代碼

    React Native仿美團(tuán)下拉菜單的實(shí)例代碼

    本篇文章主要介紹了React Native仿美團(tuán)下拉菜單的實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • ES6 class類鏈?zhǔn)嚼^承,實(shí)例化及react super(props)原理詳解

    ES6 class類鏈?zhǔn)嚼^承,實(shí)例化及react super(props)原理詳解

    這篇文章主要介紹了ES6 class類鏈?zhǔn)嚼^承,實(shí)例化及react super(props)原理,結(jié)合實(shí)例形式詳細(xì)分析了ES6 中class類鏈?zhǔn)嚼^承,實(shí)例化及react super(props)原理相關(guān)概念、原理、定義與使用技巧,需要的朋友可以參考下
    2020-02-02
  • React前端框架實(shí)現(xiàn)原理的理解

    React前端框架實(shí)現(xiàn)原理的理解

    React是前端開發(fā)每天都用的前端框架,自然要深入掌握它的原理。我用?React?也挺久了,這篇文章就來(lái)總結(jié)一下我對(duì)?react?原理的理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-07-07
  • react實(shí)現(xiàn)原生下拉刷新

    react實(shí)現(xiàn)原生下拉刷新

    這篇文章主要為大家詳細(xì)介紹了react實(shí)現(xiàn)原生下拉刷新,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論