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

React八大常見(jiàn)錯(cuò)誤及其解決方案

 更新時(shí)間:2024年03月11日 08:33:22   作者:前端貓貓教  
這篇文章主要介紹了React八大常見(jiàn)錯(cuò)誤及其解決方案,并通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)react有一定的幫助,感興趣的朋友可以參考下

我們會(huì)深度學(xué)習(xí)這 8 大錯(cuò)誤消息,包括但不限于:

  • 警告:列表中的每個(gè)子元素都應(yīng)該有一個(gè)唯一的 key 屬性
  • 防止在 key 中使用數(shù)組索引
  • React Hook useXXX 的條件調(diào)用。React Hooks 必須在每個(gè)組件渲染中以完全相同的順序調(diào)用
  • React Hook 缺少依賴(lài):“XXX”。包含它或刪除依賴(lài)數(shù)組
  • 無(wú)法對(duì)已卸載的組件執(zhí)行 React 狀態(tài)更新
  • 重新渲染次數(shù)過(guò)多。React 限制渲染次數(shù),防止無(wú)限循環(huán)
  • 對(duì)象作為 React 子元素?zé)o效/函數(shù)作為 React 子元素?zé)o效
  • 相鄰的 JSX 元素必須包含在封閉標(biāo)簽中

警告:列表中的每個(gè)子元素都應(yīng)該有一個(gè)唯一的 key 屬性

假設(shè)我們有一個(gè)卡片列表,如下所示:

import { Card } from './Card'

const data = [
  { id: 1, text: '關(guān)注' },
  { id: 2, text: '點(diǎn)贊' },
  { id: 3, text: '收藏' }
]

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

React 開(kāi)發(fā)中最常見(jiàn)的事情之一就是,獲取數(shù)組元素,并使用組件根據(jù)元素內(nèi)容渲染它們。得益于 JSX,我們可以使用 Array.map() 函數(shù)輕松將循環(huán)邏輯嵌入到組件中,并從回調(diào)中返回所需的組件。

雖然但是,在瀏覽器控制臺(tái)中收到 React 警告也司空見(jiàn)慣,提示列表中的每個(gè)子元素都應(yīng)該有一個(gè)唯一的 key 屬性。在養(yǎng)成給每個(gè)子元素一個(gè)獨(dú)特的 key 屬性的習(xí)慣之前,我們可能會(huì)多次遭遇此警告,尤其是如果我們對(duì) React 經(jīng)驗(yàn)較少。但在養(yǎng)成習(xí)慣之前該如何解決呢?

正如警告所示,我們必須將 key 屬性添加到從 map 回調(diào)返回的 JSX 的最外層元素中。雖然但是,我們要使用的 key 有若干要求。key 應(yīng)該是:

  • 字符串或數(shù)字
  • 對(duì)于列表中的特定元素而言是唯一的
  • 跨渲染列表中該元素的代表
export default function App() {
  return (
    <div className="container">
      {data.map(content => (
        <div key={content.id} className="card">
          <Card text={content.text} />
        </div>
      ))}
    </div>
  )
}

雖然如果不遵守這些要求,我們的 App 也不會(huì)崩潰,但它可能會(huì)導(dǎo)致某些意外且通常不需要的行為。React 使用這些 key 來(lái)確定列表中的哪些子元素已更改,并使用該信息來(lái)確定可以重用先前 DOM 的哪些部分,以及重新渲染組件時(shí)應(yīng)該重新計(jì)算哪些部分。因此,我們始終建議添加這些 key。

防止在 key 中使用數(shù)組索引

在上文警告的基礎(chǔ)上,我們將深入學(xué)習(xí)有關(guān) key 的同樣常見(jiàn)的 ESLint 警告。當(dāng)我們養(yǎng)成了在列表中生成的 JSX 中包含 key 屬性的習(xí)慣后,通常會(huì)出現(xiàn)下列警告。

import { Card } from './Card'

// 粉絲請(qǐng)注意,data 中沒(méi)有預(yù)生成的唯一 id
const data = [{ text: '關(guān)注' }, { text: '點(diǎn)贊' }, { text: '收藏' }]

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

有時(shí),我們的數(shù)據(jù)不會(huì)附加唯一標(biāo)識(shí)符。一個(gè)簡(jiǎn)單的解決方案就是,使用列表中當(dāng)前元素的索引。雖然但是,使用數(shù)組中元素的索引作為 key 的問(wèn)題在于,它不能代表跨渲染的特定元素。

假設(shè)我們有一個(gè)包含多個(gè)元素的列表,并且用戶(hù)通過(guò)刪除第二個(gè)元素與它們進(jìn)行交互。對(duì)于第一個(gè)元素,其底層 DOM 結(jié)構(gòu)沒(méi)有任何改變;這反映在它的 key 上,它保持不變,第一個(gè)元素的 key 還是索引 0

對(duì)于第三個(gè)元素及之后的項(xiàng)目,它們的內(nèi)容沒(méi)有改變,因此它們的底層結(jié)構(gòu)也不應(yīng)該改變。雖然但是,因?yàn)榈诙€(gè)元素被刪除了,所有其他元素的 key 屬性都會(huì)發(fā)生變化,因?yàn)檫@里的 key 是基于數(shù)組索引生成的。React 會(huì)假設(shè)它們已經(jīng)改變并重新計(jì)算它們的結(jié)構(gòu) —— 但這是不必要的。這會(huì)對(duì)性能產(chǎn)生負(fù)面影響,并且還可能導(dǎo)致不一致和不正確的狀態(tài)。

為了搞定這個(gè)問(wèn)題,最重要的是要記住,key 不一定是 id。只要它們是唯一的,并且能代表生成的 DOM 結(jié)構(gòu),我們想使用的任何 key 都問(wèn)題不大。

export default function App() {
  return (
    <div className="container">
      {data.map(content => (
        <div key={content.text} className="card">
          {/* 直接使用內(nèi)容作為 key 也能奏效 */}
          <Card text={content.text} />
        </div>
      ))}
    </div>
  )
}

React Hook useXXX 有條件地調(diào)用。React Hooks 必須在每個(gè)組件渲染中以完全相同的順序調(diào)用

我們可以在開(kāi)發(fā)過(guò)程中以不同的方式優(yōu)化我們的代碼。我們力所能及的一件事就是,確保某些代碼只在需要該代碼的代碼分支中按需執(zhí)行。尤其是在處理時(shí)間或資源密集型的代碼時(shí),這可能會(huì)在性能方面產(chǎn)生巨大的差異。

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

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

不幸的是,將這種優(yōu)化技術(shù)應(yīng)用于 Hooks,會(huì)向我們提示不要條件調(diào)用 React Hooks 的警告,因?yàn)槲覀儽仨氃诿總€(gè)組件渲染中以相同的順序調(diào)用它們。

這是必要的,因?yàn)樵趦?nèi)部,React 使用 Hook 的調(diào)用順序來(lái)跟蹤其底層狀態(tài),并在渲染之間保留它們。如果我們打亂了這個(gè)順序,React 內(nèi)部將不再知道哪個(gè)狀態(tài)與 Hook 匹配。這會(huì)給 React 帶來(lái)重大問(wèn)題,甚至可能導(dǎo)致錯(cuò)誤。

React Hooks 必須始終在組件的頂層無(wú)條件地調(diào)用。在實(shí)踐中,這通??梢詺w結(jié)為保留組件的第一部分用于 React Hook 初始化。

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

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

React Hook 缺少依賴(lài):“XXX”。包含它或刪除依賴(lài)數(shù)組

React Hooks 一個(gè)有趣的地方在于依賴(lài)數(shù)組。幾乎每個(gè) React Hook 都接受數(shù)組形式的第二個(gè)參數(shù),我們可以在其中定義 Hook 的依賴(lài)。當(dāng)任何依賴(lài)發(fā)生變化時(shí),React 會(huì)檢測(cè)到它,并重新觸發(fā) Hook。

在官方文檔中,如果變量在 Hook 中使用,React 建議開(kāi)發(fā)者始終將所有變量包含在依賴(lài)數(shù)組中,并在更改時(shí)影響組件的渲染。

為了搞定這個(gè)問(wèn)題,建議在 react-hooks ESLint plugin 中使用 exhaustive-deps rule。當(dāng)任何 React Hook 未定義所有依賴(lài)時(shí),激活它會(huì)向我們自動(dòng)發(fā)出警告。

const Component = ({ value, onChange }) => {
  useEffect(() => {
    if (value) {
      onChange(value)
    }
  }, [value])
  // onChange 沒(méi)有作為依賴(lài)包含進(jìn)來(lái)
}

我們應(yīng)該深刻認(rèn)識(shí)到,依賴(lài)數(shù)組問(wèn)題的原因與 JS 中閉包和作用域的概念有關(guān)。如果 React Hook 的主回調(diào)使用了其自身作用域之外的變量,那么它只能記住這些變量在執(zhí)行時(shí)的版本。

但是,當(dāng)這些變量發(fā)生更改時(shí),回調(diào)的閉包無(wú)法自動(dòng)獲取這些更改的版本。這可能會(huì)導(dǎo)致使用過(guò)時(shí)的依賴(lài)引用來(lái)執(zhí)行 React Hook 代碼,并導(dǎo)致與預(yù)期不同的行為。

因此,始終建議對(duì)依賴(lài)項(xiàng)數(shù)組進(jìn)行抽絲剝繭。這樣做可以搞定以這種方式調(diào)用 React Hooks 時(shí)可能出現(xiàn)的所有問(wèn)題,因?yàn)樗鼘?React 指向要跟蹤的變量。當(dāng) React 檢測(cè)到任何變量的更改時(shí),它將重新運(yùn)行回調(diào),從而允許它獲取依賴(lài)的更改版本,并按預(yù)期運(yùn)行。

無(wú)法對(duì)已卸載的組件執(zhí)行 React 狀態(tài)更新

在處理組件中的異步數(shù)據(jù)或邏輯流時(shí),我們可能會(huì)在瀏覽器控制臺(tái)中遭遇運(yùn)行時(shí)錯(cuò)誤,告訴我們無(wú)法對(duì)已卸載的組件執(zhí)行狀態(tài)更新。問(wèn)題在于,在組件樹(shù)中的某個(gè)位置,已卸載的組件會(huì)觸發(fā)狀態(tài)更新。

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

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

這是由依賴(lài)于異步請(qǐng)求的狀態(tài)更新引起的。異步請(qǐng)求在組件生命周期的某個(gè)位置開(kāi)始,比如在 useEffect Hook 內(nèi),但需要一段時(shí)間才能完成。

有多種方案可以搞定此問(wèn)題,所有這些方案都可以歸類(lèi)為兩個(gè)不同的概念。首先,可以跟蹤組件是否已安裝,我們可以據(jù)此執(zhí)行操作。

雖然這能奏效,但不建議這樣做。此方案的問(wèn)題在于,它不必要地保留未安裝組件的引用,這會(huì)導(dǎo)致內(nèi)存泄漏和性能問(wèn)題。

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

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

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

第二種方案是首選方案,是在組件卸載時(shí)取消異步請(qǐng)求。某些異步請(qǐng)求庫(kù)已經(jīng)有一個(gè)機(jī)制來(lái)取消此類(lèi)請(qǐng)求。如果是這樣,就像在 useEffect Hook 的清理回調(diào)期間取消請(qǐng)求一樣簡(jiǎn)單。

如果我們沒(méi)有使用這樣的庫(kù),我們可以使用 AbortController 實(shí)現(xiàn)同款需求。這些取消方法的唯一缺陷在于,它們完全依賴(lài)于庫(kù)的實(shí)現(xiàn)或?yàn)g覽器支持。

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

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

重新渲染次數(shù)過(guò)多。React 限制渲染次數(shù),防止無(wú)限循環(huán)

無(wú)限循環(huán)是每個(gè)開(kāi)發(fā)者的大坑,React 開(kāi)發(fā)者也不例外。幸運(yùn)的是,React 可以很好地檢測(cè)它們,并在整個(gè)設(shè)備變得無(wú)響應(yīng)之前向我們發(fā)出警告。

正如警告所示,問(wèn)題在于我們的組件觸發(fā)了太多的重新渲染。當(dāng)組件在很短的時(shí)間內(nèi)排隊(duì)太多狀態(tài)更新時(shí),就會(huì)發(fā)生這種情況。導(dǎo)致無(wú)限循環(huán)的最常見(jiàn)原因在于:

  • 直接在渲染中執(zhí)行狀態(tài)更新
  • 未向事件處理程序提供正確的回調(diào)

如果我們?cè)庥鐾罹妫?qǐng)務(wù)必檢查組件的這兩個(gè)方面。

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

  setCount(count + 1) // 在渲染中更新?tīng)顟B(tài)

  return (
    <div className="App">
      {/* onClick 沒(méi)有接受一個(gè)妥當(dāng)?shù)幕卣{(diào) */}
      <button onClick={setCount(prevCount => prevCount + 1)}>
        Increment that counter
      </button>
    </div>
  )
}

對(duì)象作為 React 子元素?zé)o效/函數(shù)作為 React 子元素?zé)o效

在 React 中,我們可以在組件中渲染很多東西到 DOM。選擇幾乎是無(wú)窮無(wú)盡的:所有 HTML 標(biāo)簽、任何 JSX 元素、任意原始 JS 值、先前值的數(shù)組,甚至 JS 表達(dá)式,只要它們被評(píng)估為任何先前值。

盡管如此,不幸的是,React 仍然不接受所有可能作為 React 子元素存在的東西。具體而言,我們無(wú)法將對(duì)象和函數(shù)渲染到 DOM,因?yàn)檫@兩個(gè)數(shù)據(jù)值不會(huì)評(píng)估為 React 可以渲染到 DOM 中的任何有意義的內(nèi)容。因此,任何這樣做的嘗試都會(huì)導(dǎo)致 React 以上述錯(cuò)誤的形式發(fā)出警告。

如果我們?cè)庥鲞@些錯(cuò)誤之一,建議驗(yàn)證我們正在渲染的變量是否是預(yù)期的類(lèi)型。大多數(shù)情況下,這個(gè)問(wèn)題是由于在 JSX 中渲染子元素或變量而引起的,假設(shè)它是一個(gè)原始值,但實(shí)際上,它是一個(gè)對(duì)象或函數(shù)。作為一種預(yù)防方法,擁有適當(dāng)?shù)念?lèi)型系統(tǒng)可以提供很大幫助。

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

相鄰的 JSX 元素必須包含在封閉標(biāo)簽中

React 最大的優(yōu)勢(shì)之一在于,能夠通過(guò)組合許多較小的組件來(lái)構(gòu)建整個(gè) App。每個(gè)組件都可以以 JSX 的形式定義它應(yīng)該渲染的 UI 部分,這最終有助于 App 的整個(gè) DOM 結(jié)構(gòu)。

const Component = () => (
  <div><NiceComponent /></div>
  <div><GoodComponent /></div>
);

由于 React 的復(fù)合性質(zhì),一個(gè)常見(jiàn)的嘗試是在一個(gè)組件的根中返回兩個(gè)僅在另一個(gè)組件中使用的 JSX 元素。雖然但是,這樣做會(huì)令人驚訝地向 React 開(kāi)發(fā)者發(fā)出警告,告訴我們必須將相鄰的 JSX 元素包裝在封閉標(biāo)簽中。

從普通 React 開(kāi)發(fā)者的角度來(lái)看,該組件只會(huì)在另一個(gè)組件內(nèi)部使用。因此,在我們的心智模型中,從組件返回兩個(gè)元素是完全有意義的,因?yàn)闊o(wú)論外部元素是在該組件還是父組件中定義,生成的 DOM 結(jié)構(gòu)都是相同的。

雖然但是,React 無(wú)法做出這個(gè)假設(shè)。該組件有可能在根中使用并破壞 App,因?yàn)樗鼘?dǎo)致無(wú)效的 DOM 結(jié)構(gòu)。

React 開(kāi)發(fā)者應(yīng)該始終將從組件返回的多個(gè) JSX 元素包裝在封閉標(biāo)記中。這可以是一個(gè)元素、一個(gè)組件或 React 的 Fragment,如果你確定該組件不需要外部元素。

const Component = () => (
  <React.Fragment>
    <div>
      <NiceComponent />
    </div>
    <div>
      <GoodComponent />
    </div>
  </React.Fragment>
)

總結(jié)

在開(kāi)發(fā)過(guò)程中邂逅 bug 是不可避免的一部分。雖然但是,我們處理這些錯(cuò)誤消息的方案也表明了作為 React 開(kāi)發(fā)者的技術(shù)修養(yǎng)。為了正確地做到這一點(diǎn),有必要了解這些錯(cuò)誤并知道它們發(fā)生的原因。

本文科普了我們?cè)?React 開(kāi)發(fā)過(guò)程中會(huì)遭遇的八大最常見(jiàn)的 React 錯(cuò)誤消息。我們介紹了錯(cuò)誤消息背后的含義、潛在錯(cuò)誤、如何解決錯(cuò)誤,以及如果不修復(fù)錯(cuò)誤會(huì)發(fā)生什么。

有了這些知識(shí)儲(chǔ)備,我們現(xiàn)在可以更徹底地理解這些錯(cuò)誤,并感到有能力編寫(xiě)更少的包含這些錯(cuò)誤的代碼,從而產(chǎn)生更高質(zhì)量的代碼。

以上就是React八大常見(jiàn)錯(cuò)誤及其解決方案的詳細(xì)內(nèi)容,更多關(guān)于React常見(jiàn)錯(cuò)誤的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 在react-router4中進(jìn)行代碼拆分的方法(基于webpack)

    在react-router4中進(jìn)行代碼拆分的方法(基于webpack)

    這篇文章主要介紹了在react-router4中進(jìn)行代碼拆分的方法(基于webpack),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-03-03
  • React useEffect使用教程

    React useEffect使用教程

    useEffect是react v16.8新引入的特性。我們可以把useEffect hook看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三個(gè)函數(shù)的組合
    2022-10-10
  • react?源碼中位運(yùn)算符的使用詳解

    react?源碼中位運(yùn)算符的使用詳解

    這篇文章主要為大家詳細(xì)介紹了react?位運(yùn)算符,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • 在React項(xiàng)目中使用TypeScript詳情

    在React項(xiàng)目中使用TypeScript詳情

    這篇文章主要介紹了在React項(xiàng)目中使用TypeScript詳情,文章通過(guò)圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • react如何添加less環(huán)境配置

    react如何添加less環(huán)境配置

    這篇文章主要介紹了react如何添加less環(huán)境配置,本文給大家分享遇到問(wèn)題及解決方案,結(jié)合示例代碼圖文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-05-05
  • React useMemo與useCallabck有什么區(qū)別

    React useMemo與useCallabck有什么區(qū)別

    useCallback和useMemo是一樣的東西,只是入?yún)⒂兴煌瑄seCallback緩存的是回調(diào)函數(shù),如果依賴(lài)項(xiàng)沒(méi)有更新,就會(huì)使用緩存的回調(diào)函數(shù);useMemo緩存的是回調(diào)函數(shù)的return,如果依賴(lài)項(xiàng)沒(méi)有更新,就會(huì)使用緩存的return
    2022-12-12
  • React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容

    這篇文章主要介紹了React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • React如何接收excel文件下載導(dǎo)出功能封裝

    React如何接收excel文件下載導(dǎo)出功能封裝

    這篇文章主要介紹了React如何接收excel文件下載導(dǎo)出功能封裝,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • React?Router?v6路由懶加載的2種方式小結(jié)

    React?Router?v6路由懶加載的2種方式小結(jié)

    React?Router?v6?的路由懶加載有2種實(shí)現(xiàn)方式,1是使用react-router自帶的?route.lazy,2是使用React自帶的?React.lazy,下面我們就來(lái)看看它們的具體實(shí)現(xiàn)方法吧
    2024-04-04
  • react?redux的原理以及基礎(chǔ)使用講解

    react?redux的原理以及基礎(chǔ)使用講解

    這篇文章主要介紹了react?redux的原理以及基礎(chǔ)使用講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評(píng)論