【React】スケルトンスクリーンを表示させる方法



## スケルトンスクリーンの実装方法

HTTPリクエストをするたにaxiosを使用します。

```:ターミナル
$ npm i axios
```

```src/urls/index.js
// リクエストに使用するURIを定義
const DEFAULT_API_LOCALHOST = 'http://localhost:3001/api/v1'

export const usersIndex = `${DEFAULT_API_LOCALHOST}/users`
```

```src/apis/users.js
import axios from 'axios';
import { usersIndex } from '../urls/index'

export const fetchUsers =() => {
  // ../urls/indexのusersIndexに記述したURLに対してリクエスト
  return axios.get(usersIndex)
  // 成功した場合には.then以降
  // 返り値をresという名前で取得し、res.dataでレスポンスの中身をreturn
  .then(res => {
    return res.data
  })
  // 失敗した場合には.catch以降
  // .catch以降でエラーをコンソールに出力
  .catch((e) => console.error(e))
}
```

状態を管理するための定数を定義します。

```src/constants.js
// 状態に関する定数を定義
export const REQUEST_STATE = {
  INITIAL: 'INITIAL',
  LOADING: 'LOADING',
  OK: 'OK',
}

```

usersReducerを作成

```src/reducers/users.js
import { REQUEST_STATE } from "../constants";

export const initialState = {
  // GET APIの状態を表す
  fetchState: REQUEST_STATE.INITIAL,
  // APIから取得したデータを配列で受け取る
  usersList: [],
};

export const usersActionTypes = {
  FETCHING: "FETCHING",
  FETCH_SUCCESS: "FETCH_SUCCESS",
};


export const usersReducer = (state, action) => {
  switch (action.type) {
    // action.typeがusersActionTypes.FETCHINGであれば
    case usersActionTypes.FETCHING:
      return {
        ...state,
        fetchState: REQUEST_STATE.LOADING,
      };
    // action.typeがusersActionTypes.FETCH_SUCCESSであれば
    case usersActionTypes.FETCH_SUCCESS:
      return {
        fetchState: REQUEST_STATE.OK,
        usersList: action.payload.users,
      };
    default:
      throw new Error();
  }
}
```

Usersコンポーネントを作成します。
スタイリングにstyled-componentsとMaterial-UIを使用しています。

```containers/Users.jsx
import React, { useEffect, useReducer } from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
// components
import { Skeleton } from '@mui/material';
// apis
import { fetchUsers } from "../apis/users";
// reducer
import { initialState, usersActionTypes, usersReducer } from "../reducers/users";
// constants
import { REQUEST_STATE } from "../constants";


// styled-componentsでスタイルを指定
const UsersContentsList = styled.div`
  display: flex;
  justify-content: space-around;
  margin-bottom: 150px;
`;

const UsersContentWrapper = styled.div`
  width: 300px;
  height: 300px;
  padding: 48px;
`;

const UsersImageNode = styled.img`
  width: 100%;
`;
const MainText = styled.p`
  color: black;
  font-size: 18px;
`;

const SubText = styled.p`
  color: black;
  font-size: 12px;
`;


export const Users = () => {
  const [state, dispatch] = useReducer(usersReducer, initialState);
  useEffect(() => {
    // dispatchはreducerを通じてstateを変更する
    // usersActionTypes.FETCHINGはreducerのcase usersActionTypes.FETCHING以下の処理を実行
    dispatch({ type: usersActionTypes.FETCHING });
    fetchUsers()
    .then((data) =>
      dispatch({
        // usersActionTypes.FETCH_SUCCESSはreducerのcase usersActionTypes.FETCH_SUCCESS以下の処理を実行
        type: UsersActionTypes.FETCH_SUCCESS,
        payload: {
          users: data.users
        }
      })
    )
  }, [])

  return (
    <>
      <UsersContentsList>
      {/* JSの変数を参照するため{}で括る */}
      {/* 三項演算子で状態によって表示を変化させる */}
        {
          state.fetchState === REQUEST_STATE.LOADING ?
            <>
            {/* fetchStateがREQUEST_STATE.LOADINGであれば以下 */}
            {/* MUIのSkeletonを使用。形やサイズを指定 */}
              <Skeleton variant="rect" width={300} height={250} />
              <Skeleton variant="rect" width={300} height={250} />
              <Skeleton variant="rect" width={300} height={250} />
            </>
          :
          /* fetchStateがREQUEST_STATE.LOADINGでなければ以下 */
          state.usersList.map((user, index) =>
            <Link to={`/users/${user.id}`} key={index} style={{ textDecoration: 'none' }}>
              <UsersContentWrapper>
                <UsersImageNode src={user.image} />
                <MainText>{user.name}</MainText>
                <SubText>{user.text}</SubText>
              </UsersContentWrapper>
            </Link>
          )
        }
      </UsersContentsList>
    </>
  )
}

```