ReactJS: Gọi API để lấy dữ liệu cho Search Form

Chúng ta sẽ cùng tìm hiểu cách gọi API để lấy dữ liệu trong React component như thê nào? Bằng cách sử dụng hookswindow.fetch.

Giả sử chúng ta sẽ cài đặt một màn hình tìm kiếm những quyển sách như sau:

Trang này đơn giản chỉ có một Form với một Input để chúng ta gõ từ khóa tìm kiếm những quyển sách hay.

Viết API client sử dụng window.fetch

Đầu tiên chúng ta cần viết một hàm API client với hai tham số là endpointcustomConfig.

function apiClient(endpoint, customConfig = {}) {
  const config = {
    method: 'GET',
    ...customConfig,
  }

  return window
    .fetch(`${process.env.REACT_APP_API_URL}/${endpoint}`, config)
    .then(async response => {
      const data = await response.json()
      // Chúng ta phải kiểm tra kết quả của response nếu ok thì trả về data
      // còn ngươc lại chúng ta trả về Promise.reject
      if (response.ok) {
        return data
      } else {
        return Promise.reject(data)
      }
    })
}

export {apiClient}

Với hàm này mỗi khi chúng ta cần gọi API để lấy data, đơn giản sử dụng apiClient và truyền vào endpoint muốn gọi. Một lưu ý là để handle error khi gọi API chúng ta phải trả về một Promise.reject như ở trên.

Viết Search Form và gọi apiClient

Chúng ta sẽ viết một form cho phép người dùng search như sau:

Bạn sẽ phải handle cho cả trường hợp success va fail khi search. Bạn có thể tham khảo code sau.

import {apiClient} from './utils/api-client'

function DiscoverBooksScreen() {
  const [status, setStatus] = React.useState('idle')
  const [data, setData] = React.useState()
  const [error, setError] = React.useState()
  const [query, setQuery] = React.useState()
  const [queried, setQueried] = React.useState(false)

  const isLoading = status === 'loading'
  const isSuccess = status === 'success'
  const isError = status === 'error'

  React.useEffect(() => {
    if (!queried) {
      return
    }
    setStatus('loading')
    apiClient(`books?query=${encodeURIComponent(query)}`).then(
      responseData => {
        setData(responseData)
        setStatus('success')
      },
      errorData => {
        setError(errorData)
        setStatus('error')
      },
    )
  }, [query, queried])

  function handleSearchSubmit(event) {
    event.preventDefault()
    setQueried(true)
    setQuery(event.target.elements.search.value)
  }

  return (
    <div
      css={{maxWidth: 800, margin: 'auto', width: '90vw', padding: '40px 0'}}
    >
      <form onSubmit={handleSearchSubmit}>
        <Input
          placeholder="Search books..."
          id="search"
          css={{width: '100%'}}
        />
        <Tooltip label="Search Books">
          <label htmlFor="search">
            <button
              type="submit"
              css={{
                border: '0',
                position: 'relative',
                marginLeft: '-35px',
                background: 'transparent',
              }}
            >
              {isLoading ? (
                <Spinner />
              ) : isError ? (
                <FaTimes aria-label="error" css={{color: colors.danger}} />
              ) : (
                <FaSearch aria-label="search" />
              )}
            </button>
          </label>
        </Tooltip>
      </form>

      {isError ? (
        <div css={{color: colors.danger}}>
          <p>There was an error:</p>
          <pre>{error.message}</pre>
        </div>
      ) : null}

      {isSuccess ? (
        data?.books?.length ? (
          <BookListUL css={{marginTop: 20}}>
            {data.books.map(book => (
              <li key={book.id} aria-label={book.title}>
                <BookRow key={book.id} book={book} />
              </li>
            ))}
          </BookListUL>
        ) : (
          <p>No books found. Try another search.</p>
        )
      ) : null}
    </div>
  )
}

export {DiscoverBooksScreen}

Chúng ta sử dụng state status để biết được các trạng thái khi gọi API như: loading, error, hay success. Và sử dụng apiClient để gọi API như chúng ta đã định nghĩa ở trên.

const [queried, setQueried] = React.useState(false)

Bạn có thể sử dụng state queried để chỉ gọi API khi mà người dùng gõ gì đó vào search input. Vì mặc định không có state queried, component sẽ gọi API ngay khi component được chạy.

Tham khảo: Bookshelf của Kent C.Dodds

Leave a Reply

Your email address will not be published. Required fields are marked *