2020/09/07

理解 React useEffect 02

前情提要

在【理解 React useEffect 01】一文了解到 useEffect 的功能是:

useEffect 主要在呈現 (Render) 後觸發此 Hook 事件,並將取得的資料後再呈現到元件上。

也知道我們要怎麼在 useEffect 裡面拉 WebAPI 的資料;且已知 useEffect 再觸發的條件是:第二個參數【依賴 dependency】發生變化時,就會再觸發 useEffect 掛勾。

這一篇將會告訴各位,如何手動觸發 useEffect,達成程式碼重用的目標。

新需求:搜尋欲下載的主題

現在已經可以在元件載入時就下載【Redux】的資料,那如果現在要下載【React】時該怎麼做?使用 Input 元件可以輸入任意資料並下載其內容。

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

export default function App() {
  const [data, setData] = useState({ hits: [] })
  const [query, setQuery] = useState("redux")

  useEffect(() => {
    async function fetchData() {
      const result = await axios(
        "https://hn.algolia.com/api/v1/search?query=redux"
      )
      setData(result.data)
    }
    console.log("hi")
    fetchData()
  }, [])

  return (
    <>
      <input value={query} onChange={ event=>setQuery(event.target.value) } />
      <ul>
        {data.hits.map((item) => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </>
  )
}

給予參數的文字框和【query】狀態連動,就能再帶入 useEffect 掛勾裡:

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

export default function App() {
  const [data, setData] = useState({ hits: [] })
  const [query, setQuery] = useState("redux")

  useEffect(() => {
    async function fetchData() {
      const result = await axios(
        `https://hn.algolia.com/api/v1/search?query=${query}`
      )
      setData(result.data)
    }
    console.log("hi")
    fetchData()
  }, [query])

  return (
    <>
      <input value={query} onChange={ event=>setQuery(event.target.value) } />
      <ul>
        {data.hits.map((item) => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </>
  )
}

axios 裡的連結參數引用狀態【query】,而且依賴 query 變化來產生對應的副作用,故在第二個參數裡填入 query。現在我們只要每輸入一個字,都會立即向 WebAPI 拉資料。

改善無效的頻寬浪費

每打一個字就向 WebAPI 拉資料會常常進行無效查詢,若是以打完文字再查詢相對合乎實際應用的場合。所以再加入一個【搜尋】按鈕來決定何時才需要向 WebAPI 拉資料。

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

export default function App() {
  const [data, setData] = useState({ hits: [] })
  const [query, setQuery] = useState("redux")
  const [search, setSearch] = useState("redux")

  useEffect(() => {
    async function fetchData() {
      const result = await axios(
        `https://hn.algolia.com/api/v1/search?query=${search}`
      )
      setData(result.data)
    }
    console.log("hi")
    fetchData()
  }, [search])

  return (
    <>
      <input value={query} onChange={ event=>setQuery(event.target.value) } />
      <button onClick={ ()=>setSearch(query) }>Search</button>
      <ul>
        {data.hits.map((item) => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </>
  )
}

增加【search】狀態,並且把【query】限縮在文字框內;Search 按鈕按下後會將 query 傳到 search,以觸發 useEffect 掛勾,進而達成 useEffect 複用的目標。

重構:設計更直覺的設計

Search 作為參數查詢,使用 url 做大包狀態更直覺:

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

const baseUrl = "https://hn.algolia.com/api/v1/search?query="

export default function App() {
  const [data, setData] = useState({ hits: [] })
  const [query, setQuery] = useState("redux")
  const [url, setUrl] = useState(baseUrl+query)

  useEffect(() => {
    async function fetchData() {
      const result = await axios(url)
      setData(result.data)
    }
    console.log("hi")
    fetchData()
  }, [url])

  return (
    <>
      <input value={query} onChange={ event=>setQuery(event.target.value) } />
      <button onClick={ ()=>setUrl(baseUrl+query) }>Search</button>
      <ul>
        {data.hits.map((item) => (
          <li key={item.objectID}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </>
  )
}

這裡說明【重構】,專案進行一段時間後,可以不定時進行程式碼重構,梳理『技術債』對自己負責。以此案例來說,把出現兩次以上的網址放到 baseUrl,減少打字的數量,並且把 search 狀態更名為 url。【React useEffect Step 3


結論

useEffect 很多文章會直接導引到 useCallback 和 useMemo 等 Hook 結合,然而有時只是需要重用 useEffect 就能解決,【How to fetch data with React Hooks?】這篇文章前半著實是 useEffect 的入門好內容,有了對它的理解和變化,在學習其它的 Hook 就能少走許多冤枉路,筆者在這裡把精髓抽取出來並加上補充說明,原文後半是 useEffect 的補充變化,給有需要自刻框架的你。

學習 useEffect的資訊整理為這兩篇,分享給各位。

 

以上。

See also

沒有留言:

張貼留言