20200330のReactに関する記事は5件です。

これからはじめる、Gatsbyのインストールから静的サイトのビルドまで

Gatsbyは次のWordpressとも言われている、Reactベースのオープンソースフレームワーク。

超高速なWebサイトやブログ、アプリを簡単に作ることができ、今最も注目されているCMSツールでもあります。

ここではGatsbyをこれからはじめる人のために、インストール〜静的サイトのビルドまでをサクッと解説していきます。

Node環境のインストール

まずは環境のチェック。nodeは11.10以降にする必要がある。

brew使ってたので、brewでnodeをアップデートする。

node入ってない人はここからダウンロードできる。

brew upgrade node

// nodeをインストールしてない場合
brew install node

インストールできたらnodeのバージョンチェック。

node -v
v13.11.0

Gatsbyのインストール

npm install -g gatsby-cli
gatsby new gatsby-site
cd gatsby-site
gatsby develop

http://localhost:8000にアクセス。ページが表示されればOK。

Home___Gatsby_Default_Starter.png

静的サイトのビルド(生成)

コマンドで自動的にファイルを作成してくれる。

gatsby build

ビルドすると、puglicフォルダに静的サイトが作成される。あとはサーバーにアップすればWebサイトを表示できる。

Gatsbyのスターターライブラリを使ったインストール

Gatsbyはデフォルトだけでなく、ブログやWebサイト、ポートフォリオサイトなどのスターターライブラリがあり、効率よく開発をスタートできる。

スターターライブラリ
https://www.gatsbyjs.org/starters/?v=2

ブログを作りたい場合はこちら。

gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog

Gatsbyのリソース

ドキュメントがしっかりしてるので、そこ見ればだいたいのことはわかる。

プラグインやライブラリなどもリンクされてるので、公式のリソース集はチェックしておきましょう。

最新情報はTwitterやRedditを活用。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【24日目】React(コンポーネント,モーダル)

挨拶

こんばんは。
progateのReactはレッスンが少ないですね。
Udemyでも学習していこうと思います。

本日の学び

progate React 3

コンポーネント

Lessonコンポーネントに渡されたpropsは「this.props.props名」で取得できる。

import React from 'react';
import Lesson from './Lesson';

class Main extends React.Component {
  render() {
    const lessonItem = {
      name: 'HTML & CSS',
      image: 'https://s3-ap-northeast-1.amazonaws.com/progate/shared/images/lesson/react/html.svg'
    }

    return (
      <div className='main-wrapper'>
        <div className='main'>
          <div className='lesson-container'>
            <Lesson
              name={lessonItem.name} {/*props名=props値*/}
              image={lessonItem.image}
            />

          </div>
        </div>
      </div>
    );
  }
}

export default Main;

モーダル

import React from 'react';

class Lesson extends React.Component { {/*Lseeonクラスの定義*/}
  constructor(props) {
    super(props);
    this.state = {isModalOpen: false}; 
   {/*モーダルが表示されているか 初期値はfalse*/}
  }

  handleClickLesson() {
    this.setState({isModalOpen: true}); {/*モーダル開けるとき*/}
  }

  handleClickClose(){
    this.setState({isModalOpen:false}); {/*モーダル閉じるとき*/}
  }

  render() {
    let modal; {/*今は空*/}
    if (this.state.isModalOpen) { {/*trueで下記が変数に入る*/}
      modal = (
        <div className='modal'>
          <div className='modal-inner'>
            <div className='modal-header'></div>
            <div className='modal-introduction'>
              <h2>{this.props.name}</h2>
              <p>{this.props.introduction}</p>
            </div>
            <button
              className='modal-close-btn'
              onClick={()=>{this.handleClickClose()}}
            >
              とじる
            </button>
          </div>
        </div>
      );
    }

    return (
      <div className='lesson-card'>
        <div
          className='lesson-item'
          onClick={() => {this.handleClickLesson()}} 
          {/*クリックでhandleClickLessonを呼び出す*/}
        >
          <p>{this.props.name}</p>
          <img src={this.props.image} />
        </div>
        {modal} {/*trueで有効*/}
      </div>
    );
  }
}

export default Lesson;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

環境変数管理の便利ツール

環境変数管理

背景

秘密鍵を使うこともあり,
sample用の.env.sampleと開発用の.envファイルを分けている

.env.sample をコミットに残すため、こちらを正として.envを管理したい

インストール方法

go get github.com/locona/envdef/cmd/envdef

使い方

テストデータ準備

プロジェクトのルートにある.envファイルにアプリケーション構成を追加します。

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE

プロジェクトのルートにある.env.sampleファイル

S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE
REGION=REGION

実行

次に実行します

envdef

その結果、.env.newファイルが作成されます

REGION=REGION
S3_BUCKET=YOURS3BUCKET
SECRET_KEY=YOURSECRETKEYGOESHERE

console にも変更点が表示されます

まとめ

envdef を使うことにより、不要になった環境変数や追加された環境変数の管理が楽になりました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Reduxには今後Redux Toolkitも使うのがいいと思う

ReactをTypeScriptで始めることが随分楽になったと感じています。Create React Appの以下のコマンドでOKです。

$ npx create-react-app my-app --template typescript

https://create-react-app.dev/docs/adding-typescript/#installation

2020年2月の中頃からReduxのテンプレートが利用可能となり、以下のコマンドで始めることができます。

$ npx create-react-app my-app --template redux
# or 
$ npx create-react-app my-app --template redux-typescript

https://github.com/reduxjs/cra-template-redux

このReduxテンプレートにはRedux Toolkitが使われています。https://redux-toolkit.js.org/
本格的に利用していくのはこれからですが、使わない場合と比べてここが便利だ(そうだ)というあたりを紹介します。

利点

初期設定が簡単

次のmiddlewareが設定済みとなります。

  • Redux Thunk
  • immutable-state-invariant
  • serializable-state-invariant-middleware

Redux Thunkは非同期通信のためのライブラリで、他2つは開発用のmiddlewareでReduxで禁止しているstateの変更を検知してくれます。

また、Redux Devtools Extensionも有効になっています。
これらを自分で適用しようとすると少々面倒です。

createSliceでActionのコードは不要に

公式ドキュメントからの抜粋となります

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
})

const store = configureStore({
  reducer: counterSlice.reducer
})

const { actions, reducer } = counterSlice
const { increment, decrement } = actions

こんな感じでAction, Reducerを定義できます。これによりほぼActionのコードがなくなるのではと期待しています。今まではComponent, Action, Reducerに対してロジックが多少入ることが気になっていましたが、Component, Reducerでそれぞれの責務でロジックを実装できそうです :smiley:

Reducerのネストはいつも通りな感じです。

const sliceA = createSlice({
  name: "reducerA",
  //...
})
const sliceB = createSlice({
  name: "reducerB",
  //...
})

import { combineReducers } from "@reduxjs/toolkit"
const mergeReducer = combineReducers({
  reducerA: sliceA.reducer,
  reducerB: sliceB.reducer,
})

非同期通信にはRedux Thunkが採用

const createUser = createAsyncThunk(
  'users/createUser',
  async () => {
    // API処理
  }
)

createAsyncThunkでは次の3つのActionを生成してくれます。

  • pending
  • fulfilled
  • rejected

sliceではextraReducersのbuilderを利用して追加します。createAsyncThunkで定義されたActionを利用するためです。

reducer.js
const userSlice = createSlice({
  name: "user",
  initialState: {
    user: null,
  },
  reducers: {},
  extraReducers: builder => {
    builder.addCase(createUser.pending, (state, action) => {})
    builder.addCase(createUser.fulfilled, (state, action) => {})
    builder.addCase(createUser.rejected, (state, action) => {})
  }
})

component.jsx
import { useDispatch } from "react-redux"

function UserComponent() {
  const dispatch = useDispatch()

  const handleSubmit = async () => {
    const resultAction = await dispatch(createUser())
    // createAsyncThunkで生成されるActionと比較して処理を変更できる
    if (createUser.fulfilled.match(resultAction)) {
      // success
    } else {
      // error
    }
  };
  return (
    <div>
      <form></form>
      <button onClick={handleSubmit}>submit</button>
    </div>
  )
}

やっておいた方がいいこと

RootReducerの型定義

TypeScriptで利用する際に用意しておいた方がいいです。以下で定義しておきます。

store.ts
import { combineReducers, configureStore } from "@reduxjs/toolkit"

export const rootReducer = combineReducers({
  moduleA: moduleAReducer,
})
export type RootReducerType = ReturnType<typeof RootReducer>

RootとなるReducerの型定義はあると便利ぐらいの気持ちです。

useDispatchのラップ

store.ts
import { configureStore } from "@reduxjs/toolkit"
const store = configureStore({
  reducer: rootReducer
})
export type AppDispatch = typeof store.dispatch
customHooks.ts
import { useDispatch } from "react-redux"
function useAppDispatch(): AppDispatch {
  return useDispatch<AppDispatch>()
}

createAsyncThunkで型定義のエラーを起こさないためです。

注意点

ImmerによるReducerのstateのImmutable

以下のようにstateを直接変更するようなコードを書いても問題ありません。Redux ToolkitではImmerを利用しており、実際にstateを変更しているわけではないとのことです。

interface CounterState {
  value: number;
}

const initialState: CounterState = {
  value: 0,
};

export const slice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: state => {
      state.value += 1;
    }
  },
});

まとめ

煩雑な設定がなくなり、ActionやReducerの実装も統一できそうです。これからReduxを始める人、そうでない人にもおすすめです :smiley:

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] 可読性の高いテストを爆速で書くための個人的テンプレート

React コンポーネントのテストを爆速で書くために個人的に従っているパターンを紹介します。サンプルでは Jest と Enzyme を使っていますが、論旨はこれらのツールに依存しないものになっているはずです。

この記事では、題材として画像のような2ステップのログイン画面をテストすることを考えます1

テストするコンポーネント

このコンポーネントはユーザ名、パスワード、今いるステップ (1ステップ目か2ステップ目か) を状態として持っているものとします。2ステップ目でユーザがログインボタンを押すと、ユーザ名とパスワードを引数にしてコールバックを呼び出します。コンポーネントのシグニチャは下記のようなものだとしましょう。

LoginForm.js
export default function LoginForm({ onLoginButtonClick }) {/*...*/}

ファクトリ関数にすべてを押し込める

テストを書き始めるにあたり、コンポーネントのマウント、モック関数の作成など、諸々のセットアップを担当するファクトリ関数を作成します。ユーザインタラクションをシミュレートするためのあらゆる面倒をこの関数に押し付けます

LoginForm.test.js
import React from "react";
import { mount } from "enzyme";
import LoginForm from "./LoginForm";

function createTestEnv() {
  // モックの作成
  const onLoginButtonClick = jest.fn();

  // コンポーネントのマウント
  const wrapper = mount(<LoginForm onLoginButtonClick={onLoginButtonClick} />);

  // ユーザインタラクションをシミュレートする便利関数
  function prompt() {
    return wrapper.text();
  }

  function enterUsername(username) {
    wrapper
      .find('input[name="username"]')
      .simulate("change", { target: { value: username } });
  }

  function enterPassword(password) {
    wrapper
      .find('input[name="password"]')
      .simulate("change", { target: { value: password } });
  }

  function clickNext() {
    wrapper.find('[children*="NEXT"]').simulate("click");
  }

  function clickLogin() {
    wrapper.find('[children*="LOGIN"]').simulate("click");
  }

  return {
    onLoginButtonClick,
    wrapper,
    prompt,
    enterUsername,
    enterPassword,
    clickNext,
    clickLogin
  };
}

テスト本体からはファクトリ関数を呼び出すだけ

上で定義したファクトリ関数を各テストの冒頭で呼び出します。簡単!

LoginForm.test.jsの続き
describe("LoginForm", () => {
  describe("the first step", () => {
    it("prompts for a username", () => {
      const { prompt } = createTestEnv();
      expect(prompt()).toContain("Enter your username");
    });
  });
  describe("the second step", () => {
    it("prompts for a password", () => {
      const { enterUsername, clickNext, prompt } = createTestEnv();
      enterUsername("alice");
      clickNext();
      expect(prompt()).toContain("Enter your password");
    });
  });
  describe("the onLoginButtonClick callback", () => {
    it("gets called with username and password", () => {
      const {
        enterUsername,
        enterPassword,
        clickNext,
        clickLogin,
        onLoginButtonClick
      } = createTestEnv();
      enterUsername("alice");
      clickNext();
      enterPassword("pAsSwOrD");
      clickLogin();
      expect(onLoginButtonClick).toBeCalledWith({
        username: "alice",
        password: "pAsSwOrD"
      });
    });
  });
});

何がうれしいのか

テスト本体の可読性が高まる

この書き方で気に入っているのは、面倒ごとをファクトリ関数に押し込むことでテスト本体の可読性が高まることです。例の中で最も長いテストでさえ自然言語として読むことができるくらいです。

      enterUsername("alice");
      clickNext();
      enterPassword("pAsSwOrD");
      clickLogin();
      expect(onLoginButtonClick).toBeCalledWith({
        username: "alice",
        password: "pAsSwOrD"
      });

実装依存が一か所にまとまる

ファクトリ関数に面倒を押し付けることの二つ目のメリットは、実装依存のコードが一か所に集まることです。UIのテストたるもの、常に「ユーザーから見えるものをテストする」のが理想ですが、現実にはそれが難しい場面も存在します。たとえば「フロッピーディスクのアイコンをクリックする」という操作をテストコードに落とし込むと、

wrapper.find('img[src="./icons/floppy-disk-icon.png"]').click();

のようにせざるを得ません。しかしここに登場する「フロッピーディスクアイコンのファイル名」は明らかに実装の詳細です。こうしたコードがテスト本体に何度も現れると、アイコンのファイル形式を png から svg に差し替えるといった軽微な変更を行うだけで、テストを何か所も変更する羽目になります。辛いですね。

一方で、実装依存のコードをファクトリ関数の中に押し込めておけば、ファクトリ関数の中の一か所を書き換えるだけでよくなります。実装依存のコードを一か所にかき集めることで、実装の変更の影響範囲を最小化できるのです。もちろん理想は実装依存が少ないテストコードです。

おわりに

私はここ最近、専ら本記事で紹介したスタイルで React コンポーネントのテストを書いています。悩む時間を減らして生産効率を上げるために、このような決まりきったパターンに従うことは有効だと感じています。スニペットを活用すればコーディングをさらに高速化できます。

このスタイルに起因する大きな問題は今のところ発生していません。しかし類例をあまり見かけないので、こうしたスタイルには問題があるものの、私がそれに気づいていないだけかもしれません。もしそのように思われる場合は、ぜひコメント欄でお知らせください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む