簡體   English   中英

如何在 React Router v6 中使用 useSearchParams Hook

[英]How to use useSearchParams Hook with React Router v6

我正在嘗試為我的 React 圖像搜索應用程序實現搜索參數功能。 而且,我了解到我需要(可以)使用 useSearchParams Hook,但我不確定如何進行這些更改。

所以,基本上我希望 URL 類似於 localhost:3000/ input &page=1,這意味着斜杠后面的任何內容都將成為頁碼的input值和鍵/值對。

正如您在 app.js 中所見,我有這 3 條主要路線,而主路線(呈現 Main.js)是我主要處理的路線。 此外,Main.js 呈現 Header.js(呈現表單和其他)。

我在想我應該在 app.js 中創建一個新的路由,但我不確定該怎么做。

import './App.css';
import Home from './components/pages/Home';
import Favorites from './components/pages/Favorites';
import Error from './components/pages/Error';
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { SkeletonTheme } from 'react-loading-skeleton';
import { useDarkMode } from './components/Navbar';


function App() {
  const darkMode = useDarkMode(state => state.darkMode)
  let style
  if (darkMode === 'light') {
    style = 'wrapper'
  } else {
    style = 'wrapper-dark'
  }

  return (
    <div className={style}>
      <SkeletonTheme baseColor="#808080" highlightColor="#b1b1b1">
        <BrowserRouter>
          <Routes>
            <Route path='/' element={<Home />} />
            <Route path='favorites' element={<Favorites />} />
            <Route path='*' element={<Error />} />
          </Routes>
        </BrowserRouter>
      </SkeletonTheme>
    </div>
  );
}

export default App;
import React from 'react'
import Header from './Header'
import Image from './Image'
import { useState, useEffect, useRef } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons'
// import InfiniteScroll from 'react-infinite-scroll-component'


function Main() {
  const [input, setInput] = useState('')
  const [allImages, setAllImages] = useState([])
  // const [totalResults, setTotalResults] = useState(null)
  const [isVisible, setIsVisible] = useState(false)
  const [error, setError] = useState(null)
  const [showError, setShowError] = useState(false)
  const [fadeOut, setFadeOut] = useState(false)
  const [page, setPage] = useState(1)
  const paginationRef = useRef(false)

  // get
  useEffect(() => {
    if (localStorage.getItem('input')) {
      setInput(JSON.parse(localStorage.getItem('input')))
    }

    if (localStorage.getItem('allImages')) {
      setAllImages(JSON.parse(localStorage.getItem('allImages')))
      // setTotalResults(JSON.parse(localStorage.getItem('totalResults')))
      setIsVisible(JSON.parse(localStorage.getItem('isVisible')))
      setPage(JSON.parse(localStorage.getItem('page')))
      paginationRef.current = true
    }
  }, [])

  // set
  //* dryer?
  useEffect(() => {
    localStorage.setItem('input', JSON.stringify(input))
  }, [input])

  useEffect(() => {
    localStorage.setItem('allImages', JSON.stringify(allImages))
  }, [allImages])

  // useEffect(() => {
  //   localStorage.setItem('totalResults', JSON.stringify(totalResults))
  // }, [totalResults])

  useEffect(() => {
    localStorage.setItem('isVisible', JSON.stringify(isVisible))
  }, [isVisible])

  function handleChange(event) {
    setInput(event.target.value)
  }

  // display nothing by default
  // display image-list when user press search button

  // function handleSubmit(event) {
  //   event.preventDefault()
  //   // interpolate input state and .env variable to API
  //   fetch(`https://api.unsplash.com/search/photos?query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
  //     .then(res => res.json())
  //     .then(data => setAllImages(data.results))
  // }

  async function fetchImages() {
    try {
      const res = await fetch(`https://api.unsplash.com/search/photos?&page=${page}&per_page=30&query=${input}&client_id=${process.env.REACT_APP_UNSPLASH_API_KEY}`)
      const data = await res.json()
      if (data.total !== 0) {
        setAllImages(data.results)
        // setTotalResults(data.total)
        setIsVisible(true)
      }
    } catch(error) {
      setError(error)
    }
  }

  const handleSubmit = async (event) => {
    event.preventDefault();
    fetchImages()
    setPage(1)
    paginationRef.current = true
  }

  // error
  useEffect(() => {
    if (error) {
      setShowError(true)
      setTimeout(() => {
        setFadeOut(true)
        setTimeout(() => {
          setShowError(false)
        }, 1000)
      }, 5000)
    }
  }, [error])

  // total results
  // let results
  // if (totalResults >= 10000) {
  //   results = 'Total Results: ' + totalResults + '+'
  // } else if (totalResults > 0) {
  //   results = 'Total Results: ' + totalResults
  // } else if (totalResults === 0) {
  //   results = 'Nothing Found'
  // }

  // pagination
  useEffect(() => {
    if (paginationRef.current) {
      fetchImages()
    }
    localStorage.setItem('page', JSON.stringify(page))
  }, [page])

  function handlePrev() {
    setPage(prevState => prevState - 1)
    fetchImages()
  }
  function handleNext() {
    setPage(prevState => prevState + 1)
    fetchImages()
  }


  return (
    <main>
      <Header
        input={input}
        handleChange={handleChange}
        handleSubmit={handleSubmit}
      />

      {showError && <div className={`network-error ${fadeOut ? 'fade-out' : ''}`}>
        <i><FontAwesomeIcon icon={faTriangleExclamation} /></i>
        <div className='network-error--message'>
          <h5>Network Error</h5>
          <p>Please check your Internet connection and try again</p>
        </div>
      </div>}

      {/* <p className='main--results'>{results}</p> */}
      <div className='main--image-list mt-5 pb-5'>
        {allImages.map(el => (
          <Image
            key={el.id}
            // do need spread operator below for img's src to work in Image.js
            {...el}
            el={el}
          />
        ))}
      </div>

      {isVisible && <div className='main--pagination'>
        <button disabled={page === 1} onClick={handlePrev}>
          Prev
        </button>
        <h5 className='main--pagination--h5'>{page}</h5>
        <button onClick={handleNext}>
          Next
        </button>
      </div>}
    </main>
  )
}

export default Main
import React from 'react'
import Navbar from './Navbar'


function Header(props) {
  return (
    <div className='header'>
      <Navbar />
      <h2 className='header--heading text-center text-light'>Find Images</h2>
      <div className='header--form'>
        <form onSubmit={props.handleSubmit}>
          <input
            className='header--form--input'
            autoComplete='off'
            type='text'
            placeholder='Search'
            onChange={props.handleChange}
            name='input'
            value={props.input}
          />
        </form>
      </div>
    </div>
  )
}

export default Header

如果您只想將page state 初始化為page queryParam,則以下方法可行。 如果使用useSearchParams訪問 queryString 並返回構造的URLSearchParams object,然后可以訪問各個查詢參數。 將“頁面”查詢參數作為初始page state 值傳遞。

const [searchParams] = useSearchParams();

const [page, setPage] = useState(Number(searchParams.get("page")) || 1);

盡管您很可能不希望針對當前頁面的內容競爭“真實來源”。 如果您希望 URL queryString 成為真實來源,則刪除page state 並直接讀取/更新“page”查詢參數。

例子:

function Main() {
  const [searchParams, setSearchParams] = useSearchParams();

  ...
 
  const page = Number(searchParams.get("page"));

  // get
  useEffect(() => {
    ...

    if (localStorage.getItem('allImages')) {
      ...
      setSearchParams(params => {
        params.set("page", JSON.parse(localStorage.getItem('page')) || 1);
        return params;
      });
      ...
    }
  }, []);

  ...

  const handleSubmit = async (event) => {
    event.preventDefault();
    ...
    setSearchParams(params => {
      params.set("page", 1);
      return params;
    });
    ...
  }

  ...

  // pagination
  useEffect(() => {
    if (paginationRef.current) {
      fetchImages();
    }
    localStorage.setItem('page', JSON.stringify(page));
  }, [page])

  function handlePrev() {
    setSearchParams(params => {
      params.set("page", Math.max(1, page - 1));
      return params;
    });
    ...
  }

  function handleNext() {
    setSearchParams(params => {
      params.set("page", page + 1);
      return params;
    });
    ...
  }

  return (
    ...
  );
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM