🚀

初めてのAxios

2020/11/26に公開

はじめに

非同期処理について勉強したいと思いAxiosをバックエンド側で導入しました!(フロントエンド側ではReactを用いています。)

プロジェクトの概要

プロジェクトは一日の予定を追加、消去できるものを作成しました。スタイルは最低限に留めていて、リストの部分にはMaterial-Uiを用いています。
reminder

ファイル構成

├── src/
   ├── components/
           └──Reminder.tsx         	   
   └── index.tsx
   └──App.tsx
   └──db.json

ルートとなるApp.tsx(コードの全体)

このAppファイルは100行を超えたのでもっとコンポーネントとして分けるべきだったと反省しています。。。
ひとまず全体をお見せします。

App.tsx
import './App.css';
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import {
  makeStyles,
  Table,
  TableBody,
  TableContainer,
  Theme,
  Typography,
} from '@material-ui/core';
import useHead from './components/Reminder';

const useStyles = makeStyles((theme: Theme) => ({
  table: {
    marginTop: theme.spacing(3),
    '& thead th': {
      fontWeight: '600',
      color: theme.palette.primary.main,
    },
  },
}));

type FormData = {
  id: number;
  reminder: string;
  time: string;
};

export type HeadCells = {
  id: number;
  label: string;
}[];

const headCells: HeadCells = [
  { id: 0, label: 'id' },
  { id: 1, label: '予定' },
  { id: 2, label: '時間帯' },
];
const data: FormData = {
  id: 0,
  reminder: '',
  time: '',
};

const App = () => {
  const classes = useStyles();

  const [reminder, setReminders] = useState<FormData[]>([]);
  const [formData, setFormData] = useState<FormData>(data);

  const axios = Axios.create({ baseURL: 'http://localhost:3000' });

  const noReminder = !reminder || (reminder && reminder.length === 0);

  const getReminders = async () => {
    const response = await axios
      .get<FormData[]>('/reminders')
      .catch((err) => console.log('err:', err));

    if (response && response.data) setReminders(response.data);
  };
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };
  const addReminder = async () => {
    const response = await axios
      .post<FormData>('/reminders', formData)
      .catch((err) => console.log('err:', err));

    if (response) getReminders();
  };
  const deleteReminder = async (id: number) => {
    const response = await axios
      .delete(`/reminders/${id}`)
      .catch((err) => console.log('Error deleting:', err));
    if (response) await getReminders();
  };
  const { CardHead, Reminder } = useHead(headCells);

  useEffect(() => {
    getReminders();
  }, []);
  return (
    <>
      <Typography variant="h4" align="center">
        今日の予定
      </Typography>

      {noReminder && <h2>No Reminders found!</h2>}
      <TableContainer>
        <Table className={classes.table}>
          <CardHead />
          <TableBody>
            {!noReminder &&
              reminder.map((reminder, id) => (
                <Reminder key={id} {...reminder} onDelete={deleteReminder} />
              ))}
          </TableBody>
        </Table>
      </TableContainer>

      <br />
      <h3>予定の追加</h3>
      <form onSubmit={addReminder}>
        <label htmlFor="id">Id</label>
        <input name="id" placeholder="Id" onChange={handleChange} />
        <label htmlFor="reminder">予定</label>
        <input name="reminder" placeholder="予定" onChange={handleChange} />
        <label htmlFor="time">時間帯</label>
        <input name="time" placeholder="Am or Pm" onChange={handleChange} />
        <button type="submit">追加</button>
      </form>
    </>
  );
};

export default App;

コードの詳細

部分的にコードを見ていきます。
まず、createメソッドを用いていインスタンスを作成します。

createメソッド
const axios = Axios.create({ baseURL: 'http://localhost:3000' });

そしてデータ(id,reminder,time)はdb.jsonに格納しています。JSON Serverの立ち上げ方についてはこちらの記事JSON Server使いこなしで勉強させていただきました。
このjsonファイルに追加、削除の動作をaxiosのgetメソッドdeleteメソッドが担ってくれます。山括弧にはTypeScriptのジェネリック型を指定しています。

get,deleteメソッド
const getReminders = async () => {
    const response = await axios
      .get<FormData[]>('/reminders')
      .catch((err) => console.log('err:', err));

    if (response && response.data) setReminders(response.data);
  };
  
  const deleteReminder = async (id: number) => {
    const response = await axios
      .delete(`/reminders/${id}`)
      .catch((err) => console.log('Error deleting:', err));
    if (response) await getReminders();
  };

getメソッドを使っているgetReminders関数ですが,レンダーの結果が画面に反映された後に動作するようにuseEffectを用いています。またuseEffecの第二引数には、依存する唯一の変数であるsetReminersを指定しています。

useEffect(() => {
  getReminders();
}, [setReminders]);

次にデータを登録するためにpostメソッドを使用します。第2引数には登録するデータを渡します。

const addReminder = async () => {
    const response = await axios
      .post<FormData>('/reminders', formData)
      .catch((err) => console.log('err:', err));

    if (response) getReminders();
  };

この非同期関数(addReminder)はformタグのonSubmit属性に当てており、個々のデータ(id,reminder,time)の変化はinputタグのonChange属性にhandleChange関数を割り当てています。

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

//JSX.Element
<form onSubmit={addReminder}>
 <label htmlFor="id">Id</label>
 <input name="id" placeholder="Id" onChange={handleChange} />
  <label htmlFor="reminder">予定</label>
  <input name="reminder" placeholder="予定" onChange={handleChange} />
  <label htmlFor="time">時間帯</label>
  <input name="time" placeholder="Am or Pm" onChange={handleChange} />
  <button type="submit">追加</button>
</form>
    </>

このhandleChange関数の作成については前々回の記事にて述べています!
フォームを作る過程でつまずいたこと

あとuseReminder関数は別ファイル(Reminder.tsx)からimportしています。

App.tsxの一部
import useReminder from './components/Reminder';const { CardHead, Reminder } = useReminder(headCells);

以下Reminder.tsxファイルの全体です。

Reminder.tsx
import { Grid, TableCell, TableHead, TableRow } from '@material-ui/core';
import React from 'react';
import DeleteForeverIcon from '@material-ui/icons/DeleteForever';
import { HeadCells } from '../App';

interface Props {
  id: number;
  reminder: string;
  time: string;
  onDelete: (id: number) => any;
}

const useReminder = (headCells: HeadCells) => {
  const CardHead: React.FC = () => {
    return (
      <TableHead>
        {headCells.map(({ id, label }) => (
          <TableCell key={id} align="center">
            {label}
          </TableCell>
        ))}
      </TableHead>
    );
  };

  const Reminder: React.FC<Props> = ({ id, reminder, time, onDelete }) => {
    return (
      <>
        <TableRow key={id}>
          <TableCell align="center">{id}</TableCell>
          <TableCell align="center">{reminder}</TableCell>
          <TableCell align="center">{time}</TableCell>
          <Grid className="reminder-remove" onClick={() => onDelete(id)}>
            <DeleteForeverIcon />
          </Grid>
        </TableRow>
      </>
    );
  };

  return { CardHead, Reminder };
};

export default useReminder;

このような子コンポーネントを一つの関数としてラップする関数(useReminder)の作成については前回記事にて述べていますので、是非御覧ください!Propsをよりスマートに渡したい!

以上、コードの詳細でした。ここまで読んでいただきありがとうございました!!

Discussion