こんにちは、フロントエンドエンジニアの仲本です。
Wizの新プロジェクトにてフロントエンドをReactを使用して実装しました。
新プロジェクトのフェーズ1が終了とフェーズ2の間で一度リファクタリングを行おうと思い今回コードレビュー会を開きました。
コードレビュー以外にもNext.jsについての話も盛り上がったのですが、今回はコードレビューの内容のみお届けします。
コードレビューでご指摘していただいた中から2つほど紹介いたします。
使用技術
- React
- Redux-toolkit
- Emotion
fetchの共通化
ある程度fetchをする回数が増えてくると、bodyを書いたりheadersでcontent-typeを毎回書くのはつらなくなってきます。
なので、fetchを共通化させます。
修正前
こちらがログイン処理などをまとめているsliceになります。
現在例で出している処理は、違うタイプの処理ではありますが認証タイプの処理は他のAPIで使用することもあるので共通化させておくと実装する際に楽になります。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import { apiURL } from '../../utils/constants' export interface loginFormInput { mailAddress: string; passWord: string; } export const fetchAsyncLogin = createAsyncThunk('login', async (data: loginFormInput) => { // ログインAPI const loginParams = { login_id: data.mailAddress, password: data.passWord } const res = await fetch(`${apiURL}/login`, { method: 'POST', body: JSON.stringify(loginParams), headers: { 'Content-Type': 'application/json' }, }) return res.json() }) export const fetchAsyncAuth = createAsyncThunk('auth', async (data: {token: string}) => { // 認証API const res = await fetch(`${apiURL}/auth`, { method: 'POST', mode: 'cors', headers: { Authorization: `Bearer ${data.token}` }, }) return res.json() }) ~~省略~~
修正後
共通化用ファイルを作成します。
// post用 認証なし export const fetchPostNoAuth = async (url: string, params) => { const res = await fetch(url, { method: 'POST', body: JSON.stringify(params), headers: { 'Content-Type': 'application/json' }, }) return res.json() } // 認証付きAPI bodyなし export const noBodyFetch = async (url: string, token: string, method: 'GET'|'PATCH'|'POST') => { const res = await fetch(url, { method: method, mode: 'cors', headers: { Authorization: `Bearer ${token}` } }) return res.json() } // 認証付きAPI bodyあり export const bodyFetch = async (url: string, token: string, method: 'POST' | 'PATCH', body: string) => { const res = await fetch(url, { method: method, mode: 'cors', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }, body: body }) return res.json() }
共通化を行ったら、先程のsliceも変更します。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit' import Cookies from 'js-cookie' import { apiURL } from '../../utils/constants' import { fetchPostNoAuth, noBodyFetch } from '../../../ts/fetch' // 非同期はSliceの外に出してcreateAsyncThunkを使用する export interface loginFormInput { mailAddress: string; passWord: string; }; export const fetchAsyncLogin = createAsyncThunk('login', async (data: loginFormInput) => { // ログインAPI const loginParams = { login_id: data.mailAddress, password: data.passWord } const url = `${apiURL}/login` const fetchLogin = fetchPostNoAuth(url, loginParams) return fetchLogin }) export const fetchAsyncAuth = createAsyncThunk('auth', async (data: {token: string}) => { // 認証API const url = `${apiURL}/auth` return noBodyFetch(url, data.token, 'POST') })
結構スッキリしました。
Error Boundaryの追加
最後にErrorBoudaryの追加です。
ErrorBoudaryとは
自身の子コンポーネントツリーで発生した JavaScript エラーをキャッチし、エラーを記録し、クラッシュしたコンポーネントツリーの代わりにフォールバック用の UI を表示する React コンポーネントです。 (引用: React公式|Error Boudanry )
React16からコンポーネント内でエラーが発生した場合、React コンポーネントツリー全体がアンマウントされてしまうので追加する必要があります。
実装
import React from 'react' import { ErrorBlock } from './views/components/block/Error'; type Props = { children: JSX.Element } class ErrorBoundary extends React.Component<Props, { hasError: boolean }> { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } render() { if (this.state!.hasError) { return <ErrorBlock />; } return this.props.children; } } export default ErrorBoundary
上記がErrorBoudary検知用のコンポーネントになります。 コードの説明します。
- getDerivedStateFromError: エラーを検知した時に走る処理
- ErrorBlock:コンポーネント内でエラーが発生した場合に表示するコンポーネント
- props.children ErrorBoundaryでコンポーネントを囲った際の中身
あとはこちらをエラー時にマウントさせたい場所でimportします。
import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import store from './stores' import { jsx } from '@emotion/react' import GlobalStyle from './style/GlobalStyle' import 'react-hot-loader' const app = document.getElementById('app') //component import Routing from './Routing' import ErrorBoundary from './ErrorBoundary' ReactDOM.render( <ErrorBoundary> <Provider store={store}> <GlobalStyle /> <Routing /> </Provider> </ErrorBoundary> , app ) if (module.hot) { module.hot.accept() }
おわりに
以上がコードレビュー会での指摘された内容でした。
今後はredux周りのパフォーマンスチューニングを行っていきたいと思っています。 またその際は記事にしてアップしたいと思います。
ここまで読んで頂き、ありがとうございます。
Wizではエンジニアを募集しております。
興味のある方、ぜひご覧下さい。