iT邦幫忙

2022 iThome 鐵人賽

DAY 19
0
自我挑戰組

SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭系列 第 19

[Day19] 如何用 React Router 在網址列加上 ?q= 的參數

  • 分享至 

  • xImage
  •  

前言

一樣開始前請記得在 local 端開好前端、後段:
後端:json-server --watch db.json --port 10000 --routes routes.json
前端:npm start

本日正文

今天的文章就是要補昨天沒完成的洞─如何在網址列加上我們搜尋的關鍵字,
再拿這個關鍵字去 axios.get

react-router-dom

為了要控制網址列跟取得網址列的參數,
首先我們要先安裝及 import react-router-dom 套件,
像這樣:
https://ithelp.ithome.com.tw/upload/images/20220920/20129873TvmJSJxgzy.png

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link,
  useLocation,
} from 'react-router-dom';

再來重點就是我們要在按下「搜尋」按鈕時,
一併把 ?q= 的參數也顯示在網址列當中,
昨天文章中搜尋列跟搜尋按鈕是這樣寫的:

<Flex w="50%">
  <Input placeholder='請輸入要搜尋的關鍵字...' size='md' onChange={(e) => setSearchKeywords(e.target.value)} />
  <Button colorScheme="blue" onClick={() => { searchDataKeywords(); }}>搜尋</Button>
</Flex>

今天我們改寫成這樣:

<Flex w="50%">
  <Input placeholder='請輸入要搜尋的關鍵字...' size='md' onChange={(e) => setSearchKeywords(e.target.value)} />
  <Router>
    <Routes>
        <Route element={<SearchButton keyWords={searchKeywords} />} path={'/'}></Route>
    </Routes>
  </Router>
</Flex>

Router, Routes, Route:定義連結的路由

主要是增加了 Router 的元素:

<Router>
    <Routes>
        <Route element={<SearchButton keyWords={searchKeywords} />} path={'/'}></Route>
    </Routes>
  </Router>

這段的意思是要建立路由 Router
一個路由通常會有很多路徑 Routes
Routes 我們將每一條 Route 定義好,
而各別的 Route 外觀會長怎樣,
就是由 element 決定,
path 就是用來比對路徑,也就是說這個 Route 要在 / 才顯示,
如果是在別的連結,例如 /about 是不會顯示的,
像這樣,搜尋按鈕只出現在 / 的頁面, /about 沒有顯示:
https://ithelp.ithome.com.tw/upload/images/20220920/20129873fdIDxaURrv.png

Link:導向連結

element 裡面到底寫了什麼呢?

element={<SearchButton keyWords={searchKeywords} />

這邊拆解一下,我們宣告了一個 SearchButton 的 component,
然後可以想像的是裡面我們一定有宣告「搜尋」的按鈕,
像這樣:

const SearchButton = ({keyWords}) => {
  return (
      <div>
        <Link to={`/?q=${keyWords}`}>
        <Button onClick={() => { 
          setTimeout(() => {
            setIsSearchOn(true);
          }, 300)
          }}>搜尋</Button>
        </Link>
          
      </div>
  )
}

這邊要注意的是,在 <Button> 外面由 <Link> 包住,
意思是,當我按下 <Link> 裡面的元件後,網址前往 to={`/?q=${keyWords}`} 指向的連結,
(PS. keyWords 是由 <SearchButton keyWords={searchKeywords} /> 傳入的,
searchKeywords 就是我們在搜尋列所輸入的文字)

這邊我們試試看我們輸入文字並按下搜尋會出現怎樣的變化吧:
https://ithelp.ithome.com.tw/upload/images/20220920/20129873rXI73uX0Cm.png

是的,你可以看到網址列已經出現 ?q=飯 的字眼了,
再來就是重頭戲,我們要如何拿到網址列的這個參數再進行後續處理。

useLocation:拿到 pathname, search

首先我們要利用 react-router-dom 裡面的 useLocation

const location = useLocation();
console.log(location);

那我們一樣先用 console.log 來看一下 location 印出來的值:
https://ithelp.ithome.com.tw/upload/images/20220920/20129873UUVFyMw6Sv.png

你可以發現裡面有幾個值,其中 pathname 是代表現在在哪個路徑,
search 是網址列帶的參數,
所以看起來 search 就是我們要拿來利用的。

再來這邊有點 tricky,我就稍微帶過去講一下。
本來我是這樣寫的:

const location = useLocation();
  console.log(location);
  const searchParam = location.search;

  axios.get(`${hostUrl}/api/v1/sales${searchParam}`)
      .then(function (response) {
          setSales(response.data);
      })
      .catch(function (error) {
          alert("抓取資料錯誤,請確認後再試");
          console.log(error);
      })
      setIsSearchOn(false);
    }

看起來很美好對吧,就是我們拿著 location.search 的值,
帶入到 axios.get 的 url 中,
應該就可以收工了?

用 setTimeout 延遲執行的時間

但我在測試的時候發現,
按下搜尋的那個瞬間,
其實 url 還沒變成新的網址,
例如當我上一次輸入飯,這次輸入麵,
但你會發現你拿到的值還是"飯",而不是這次輸入的"麵",
所以我多宣告了一個變數 isSearchOn

const [isSearchOn, setIsSearchOn] = useState(false);

當我按下搜尋時,只是去控制這個 isSearchOn 的開關,
且故意延遲 setTimeout 後才去執行 setIsSearchOn
(避免太快去執行 axios.get)

<Button onClick={() => { 
  setTimeout(() => {
    setIsSearchOn(true);
  }, 300)
  }}>搜尋</Button>

isSearchOntrue 時,
才真的去 call API axios.get
call 完才把 isSearchOn 設為 false
所以這邊完整個程式碼是這樣的:

const SearchButton = ({keyWords}) => {
  const location = useLocation();
  console.log(location);
  const searchParam = location.search;

  if (isSearchOn){
      axios.get(`${hostUrl}/api/v1/sales${searchParam}`)
          .then(function (response) {
              setSales(response.data);
          })
          .catch(function (error) {
              alert("抓取資料錯誤,請確認後再試");
              console.log(error);
          })
          setIsSearchOn(false);
  }
  return (
      <div>
        <Link to={`/?q=${keyWords}`}>
        <Button onClick={() => { 
          setTimeout(() => {
            setIsSearchOn(true);
          }, 300)
          }}>搜尋</Button>
        </Link>
      </div>
  )
}

那我們一樣來看看效果如何吧:

把昨天的債還掉了,
那明天文章再見囉!

參考文章

React 中優雅使用網址參數 Query String
React Router
新版 React router 怎麼用? React-router-dom v6!
React Router 與 Hook 的邂逅
Day 19-你好,React Router 4(安裝及角色介紹)
(↑偶然找到有大大在鐵人賽講到 React Router 的文章,而且剛好跟我一樣是第19天,也太巧XD)
REMIX / REACT ROUTER
(↑不過這好像是 V5 的寫法,<Switch> 的寫法在 V6 已經不適用的樣子)

後記

終於寫出來了orz
其實用 Next.js 提供的 next/router
應該可以更快寫完,但一直卡關,
所以只好拿 React Router 來寫了,
然後因為從來沒用過 React Router
所以也是試滿久的,
查了查發現 React Router 好像有大改版過,
所以大大們的寫法都不太一樣,
試了一陣子才找到可以 work 的寫法orz
因此如果我有錯誤之處請大大們不吝鞭策與指教(跪)
每天都離天窗更近了QQ


上一篇
[Day18] call API 會用的參數系列~用 ?q= 來做關鍵字搜尋
下一篇
[Day20] 用 React Router + useForm + axios.post 打造一個簡易後台
系列文
SPYxFRONTEND ~ 懂一點後端真是讓人哇哭哇哭30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言