ReactHookFormでクエリパラメータを更新し、API通信する。

## はじめに
表題通り、ReactHookFormでクエリパラメータを更新し、クエリパラメータの変更を検知する事でAPI通信する仕組みを作ります。やりたい事は下記の図の通りです。

![スクリーンショット 2024-06-18 21.46.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/e47ddb83-1b6c-5ee4-cda0-165840c9c9fd.png)

## 成果物
![jsonplaceholder.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/7c2afafa-13fd-3a31-d79a-19dc38b6429d.gif)

https://github.com/kouji0705/react_hook_form_sample/pull/1

## ソースコード

```tsx:src/App.tsx
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Container, Typography } from '@mui/material';
import { JsonPlaceHolder } from './components'

const App: React.FC = () => {
  return (
    <Router>
      <Container>
        <Typography variant="h4" component="h1" gutterBottom>
          Search Example with React Hook Form
        </Typography>
        <Routes>
          <Route path="/" element={<JsonPlaceHolder />} />
        </Routes>
      </Container>
    </Router>
  );
};

export default App;

```

```tsx:src/components/index.tsx
import { useQueryParams, useFetchData } from "./hooks"
import SearchForm from "./SearchForm"
import { SearchResult } from "./SearchResult"


export const JsonPlaceHolder = () => {
  const query = useQueryParams();
  const { results, loading } = useFetchData(query);

  return (
    <div>
      <h1>JsonPlaceHolder</h1>
      <SearchForm />
      <SearchResult results={results} loading={loading} />
    </div>
  );
};
```

```tsx:src/components/SearchForm.tsx
import { useForm } from 'react-hook-form';
import { TextField, Button, Box } from '@mui/material';
import { useLocation, useNavigate } from 'react-router-dom';
import queryString from 'query-string';

type SearchFormData = {
  query: string;
};

const SearchForm = () => {
  const location = useLocation();
  const navigate = useNavigate();
  const params = queryString.parse(location.search);
  const defaultQuery = params.query as string || '';

  const { register, handleSubmit } = useForm<SearchFormData>({
    defaultValues: { query: defaultQuery }
  });


  const onSubmit = (data: SearchFormData) => {
    const newQuery = queryString.stringify({ query: data.query });
    navigate(`?${newQuery}`);
  };
  return (
    <Box>
      <form onSubmit={handleSubmit(onSubmit)}>
        <TextField
          label="Search"
          {...register('query')}
          variant="outlined"
          fullWidth
          margin="normal"
        />
        <Button type="submit" variant="contained" color="primary">
          Search
        </Button>
      </form>
    </Box>
  );
};

export default SearchForm;
```

```tsx:src/components/SearchResult.tsx
import { Box, CircularProgress, List, ListItem, ListItemText } from '@mui/material';

type SearchResultProps = {
    results: any[];
    loading: boolean;
};

export const SearchResult = ({ results,loading}:SearchResultProps) => {
  return (
    <Box mt={4}>
      {loading ? (
        <Box display="flex" justifyContent="center">
          <CircularProgress />
        </Box>
      ) : (
        <List>
          {results.map((result) => (
            <ListItem key={result.id}>
              <ListItemText primary={result.title} />
            </ListItem>
          ))}
        </List>
      )}
    </Box>
  );
};

```

```tsx:hooks.tsx
import { useLocation } from "react-router-dom";
import { useState, useEffect } from "react";
import queryString from "query-string";
import axios from "axios";

export const useQueryParams = () => {
    const location = useLocation();
    const params = queryString.parse(location.search);
    const queryParam = params.query as string || '';
  
    return queryParam;
  };
  
 export const useFetchData = (query: string) => {
    const [results, setResults] = useState<any[]>([]);
    const [loading, setLoading] = useState<boolean>(false);
  
    useEffect(() => {
      const fetchData = async () => {
        setLoading(true);
        try {
          const response = await axios.get('https://jsonplaceholder.typicode.com/posts', {
            params: { q: query }
          });
          setResults(response.data);
        } catch (error) {
          console.error('Error fetching data:', error);
          setResults([]);
        } finally {
          setLoading(false);
        }
      };
  
      if (query) {
        fetchData();
      } else {
        setResults([]);
      }
    }, [query]);
  
    return { results, loading };
  };
  
```