りゅうじの学習blog

学習したことをアウトプットしていきます。

6/21 コンポーネント駆動開発、簡易な導入手順まとめ

使うライブラリ・パッケージと概要

  • React, TypeScript, Storybook: ReactはJavaScriptライブラリであり、ユーザーインターフェイスの構築を容易にします。TypeScriptはJavaScriptのスーパーセットで、型安全性を提供し、バグを早期に発見し、コードのリーダブル性と保守性を向上します。StorybookはUIコンポーネントの開発とドキュメンテーションを支援するツールです。Storybookを使用すると、各コンポーネントの異なる状態を視覚的に確認することができます。
  • Jest: JestはJavaScriptのテストフレームワークで、Reactコンポーネントのテストに特に適しています。Jestはユニットテスト結合テストの両方をサポートしており、モック関数やスナップショットテストなどの機能も提供しています。
  • Plop: Plopはボイラープレートコードを生成するためのツールです。あらかじめ定義したテンプレートを元に新しいコンポーネントやテストの雛形を簡単に生成することができます。
  • MSW (Mock Service Worker): MSWはブラウザとNode.jsで動作するモック/スパイライブラリです。MSWはネットワークリクエストを補足し、定義したモックレスポンスを返します。これにより、APIの実装が未完成でもフロントエンドの開発やテストを進めることができます。

導入手順

React TypeScriptプロジェクトの作成

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

TypeScriptのオプションをつけることで、作成時からTypeScriptの初期設定やファイル名になります。

Storybookのインストール

npx sb init

.storybook ディレクトリが作成される。

また、既存のデフォルトのストーリーは src/stories ディレクトリ内に格納される。

Jestのインストール:

Jestは create-react-app にデフォルトで含まれていますが、追加の設定や型定義が必要な場合は次のコマンドを実行Jestは create-react-app にデフォルトで含まれていますが、追加の設定や型定義が必要な場合は次のコマンドを実行

Plopのインストールと設定:

npm i -D plop

プロジェクトのルートディレクトリに plopfile.js ファイルを作成

touch plopfile.js

plopfile.js

module.exports = function (plop) {
    // コンポーネントジェネレータを作成します
    plop.setGenerator('component', {
        description: 'Create a component',
        prompts: [{
            type: 'input',
            name: 'name',
            message: 'What is your component name?'
        }],
        actions: [
            {
                type: 'add',
                path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.tsx',
                templateFile: 'plop-templates/Component.tsx.hbs'
            },
            {
                type: 'add',
                path: 'src/components/{{pascalCase name}}/{{pascalCase name}}.stories.tsx',
                templateFile: 'plop-templates/Component.stories.tsx.hbs'
            }
        ]
    });
};
  • Plopの設定ファイルで、このファイルによってPlopがどのように動作すべきかを定義
    • このファイル内で、生成されるファイルのタイプや名前、テンプレートとなるファイルへのパス、プロンプトの質問などを設定している

plop-templatesというディレクトリをプロジェクトのルートに作成

その中にテンプレートとなるComponent.tsx.hbsComponent.stories.tsx.hbsという2つのファイルを作成

これらの.hbsファイルはHandlebarsテンプレートと呼ばれ、特定の箇所を置換して新しいファイルを生成するために使用される

基本的な例

Component.tsx.hbs

import React from 'react';

export interface {{pascalCase name}}Props {

}

export const {{pascalCase name}}: React.FC<{{pascalCase name}}Props> = (props) => {
    return (
        <div>
            {/* Implement your component here */}
        </div>
    );
}

Component.stories.tsx.hbs

import React from 'react';
import { {{pascalCase name}}, {{pascalCase name}}Props } from './{{pascalCase name}}';

export default {
    title: 'Example/{{pascalCase name}}',
    component: {{pascalCase name}},
};

const Template: React.VFC<{{pascalCase name}}Props> = (args) => <{{pascalCase name}} {...args} />;

export const Primary = Template.bind({});
Primary.args = {
    /* Fill in your default args here */
};

npx plop componentコマンドを実行すると、ファイル名を聞かれるので、例としてbuttonと答えたとすると、Button.tsxButton.stories.tsxsrc/components/Button/ディレクトリに作成される

ストーリーブックのテスト設定

ストーリーブックのテストには、@storybook/addon-storyshotsを使用する。これをインストールして、自動的にStorybookのスナップショットテストを作成できる。

npm install --save-dev @storybook/addon-storyshots react-test-renderer

srcディレクトリにstoryshots.test.jsという新しいファイルを作成

touch src/storyshots.test.js

storyshots.test.jsファイルに以下の内容を追加

import initStoryshots from '@storybook/addon-storyshots';

initStoryshots();

この設定により、Jestがテストを行うときにStorybookの各ストーリーのスナップショットテストを自動的に行う

テストの実行:

npm test
  • start test などは、package.jsonのscriptsに書かれていて、runをつけなくてもいい
  • このコマンドでjestのテストが走ります

storybook起動

npm run storybook
  • このコマンドをするとブラウザにstorybookが立ち上がるので、現状のstorybook(今回の例だとbuttonコンポーネント)が反映される

mswの設定:

npm install msw

mswを設定します。以下のコードをsrc/mocks/handlers.jsに追加します。

import { rest } from 'msw'

export const handlers = [
  rest.post('https://example.com/api/submit', (req, res, ctx) => {
    return res(ctx.json({ message: 'Success' }))
  }),
]

このハンドラをテスト環境で使用できるように設定します。以下のコードをsrc/mocks/browser.jsに追加します。

import { setupWorker } from 'msw'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)

テストのセットアップとクリーンアップでmswサーバーを起動と停止します。以下のコードをsrc/setupTests.jsに追加します。

import { server } from './mocks/server.js'

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

これでmswがテストで利用可能になりました。

基本的なコード例

import React from 'react'
import { render, fireEvent, waitFor } from '@testing-library/react'
import { rest } from 'msw'
import { server } from './mocks/server'
import Button from './Button'

test('submits the form when the button is clicked', async () => {
  server.use(
    rest.post('https://example.com/api/submit', (req, res, ctx) => {
      return res(ctx.json({ message: 'Success' }))
    })
  )

  const mockOnClick = jest.fn()
  const { getByText } = render(<Button

コメント

実務でフロントエンドをCDDで開発することになったので、一回自分で小さく動かしてみるために、設定方法を簡単にまとめました。

6/20 Growthマインドセット

前提として成長の定義は

  • できなかったことを、できるようになること

Growth(成長)マインドセットとは

  • 会社の成功に必要なスキルを自分から積極的に学び、習得しようとするマインドセット

ある人とない人の違い

  • 新しい挑戦に対して、チャンスと捉えるか、それとも面倒だと感じるか

持っているかの見極めポイント

  • 過去18ヶ月で、元々のジョブディスクリプションには含まれていなかったけれど、会社の成功のために新たに身につけたスキルはありますかとストレートに聞く

持っている人の共通点

  • 「自分が正しい」といった固定概念を捨てられること
    • 一定の成功体験はもちろん自信につながるが、世の中はどんどん変化していくから、もっと良い方法はないか、もっと面白いアクションはないかという思考プロセスを繰り返す能力が重要
  • 一次情報を取りに行き、新しいものを学ぶ姿勢があること
    • それを楽しんで「こんな新しいものを見つけたよ」「これが面白かったよ」と新しい発見を共有してくれる
  • 学んだことを言語化することも得意
    • 自身の学びや新しい発見を、他者に対して還元していこうというマインドが強い

Growthマインドセット」を持つ人材を増やすために、工夫や大切にしていること

  • 痛みを伴うような経験をポジティブに乗り越えようとする考え方
    • フィードバックを「自分が攻撃されている」と受け取るのではなく、「自分の成長のために何かをしてもらっている」と前向きに捉える

参考

【3人のリーダーに聞く!】競争市場で勝ち抜くための「Growthマインドセット」人材、採用と育成のポイント

6/20 TCP/IP Chapter1 part4

PDU

  • 各階層で細かく分割されて、データは処理される
    • その一つの塊をPDU(Protcol Data Unit)と呼ばれる

PDUは処理される階層により名称が異なります。

参考

仕組み・動作が見てわかる 図解入門 TCP/IP

Jest DOMの主なカスタムマッチャー

例えばこちらですと、test1 test2 が表示されている(見える)ことを確認しています。

describe('TopPage', () => {
  test('デフォルト(テストケース)', async () => {
    render(<Default />);
    await waitFor(() => {
      expect(screen.getByText('test1')).toBeVisible();
      expect(screen.getByText('test2')).toBeVisible();
    });
  });
  1. toBeInTheDocument(): 要素がDOMに存在することを確認します。
  2. toBeVisible(): 要素が表示されている(見える)ことを確認します。
  3. toBeEmpty(): 要素が空(子要素を持たない)ことを確認します。
  4. toBeEmptyDOMElement(): 要素が空(子要素やテキストを持たない)ことを確認します。
  5. toBeInvalid() / toBeValid(): 要素が無効または有効な状態であることを確認します。
  6. toBeRequired(): 要素が必須(required属性を持つ)であることを確認します。
  7. toBeEnabled() / toBeDisabled(): 要素が有効または無効化(disabled)されていることを確認します。
  8. toContainElement(element): 要素が特定の他の要素を含むことを確認します。
  9. toContainHTML(html): 要素が特定のHTMLを含むことを確認します。
  10. toHaveAttribute(attributeName, value?): 要素が特定の属性を持つことを確認します。オプションで属性の値も指定できます。
  11. toHaveClass(className): 要素が特定のクラス名を持つことを確認します。
  12. toHaveFocus(): 要素がフォーカスされていることを確認します。
  13. toHaveFormValues(expectedValues): フォーム要素が特定の値を持つことを確認します。
  14. toHaveStyle(style): 要素が特定のスタイルを持つことを確認します。スタイルは文字列またはオブジェクトで指定できます。
  15. toHaveTextContent(text): 要素が特定のテキスト内容を持つことを確認します。
  16. toHaveValue(value): フォームコントロール要素(<input><textarea>など)が特定の値を持つことを確認します。
  17. toHaveDisplayValue(value): フォームコントロール要素が特定の表示値を持つことを確認します。
  18. toBeChecked() / toBePartiallyChecked(): チェックボックスラジオボタンがチェックされている、または部分的にチェックされていることを確認します。
  19. toHaveDescription(description): 要素が特指定のアクセシビリティの説明(aria-describedbyで参照される要素のテキスト)を持つことを確認します。

コメント

フロントエンドのテストを極めたいので、少しずつ理解を深めていきます。

6/17 lodashのvalues

見るたびに何となく理解しては、時間が経つと忘れてしまっているのでまとめます。

インストール

npm i lodash @types/lodash

コード

import { values } from "lodash";

const obj = {
    a: 1,
    b: 2,
    c: 3,
  };

  const valArray = values(obj);

デベロッパーツールで見た出力結果

Array(3)
0: 1
1: 2
2: 3
length: 3

コメント

最近、頭の中で完結させるより、簡単な実例に習って、ハンズオンを軽く行った方がいいような気がしている。

考えるより体験を!というような感覚です。

6/15 RuduxToolKitの忘れかけていた箇所のおさらい

 

 

  • storeの中にreducerがある
    • なので実際の実装では、configureStore内にreducerを設定する

 

  • sliceにはstate reducer actioncreatorがある
    • sliceの中にreducerがあるので、sliceからreducerを取り出して、storeに入れる
      • なので実際の実装では、exportの際にreducerのみを取り出して、storeに入れている

 

# 参考

Udemy

6/14 Storybook const Template = (args) => <Button {...args} />;について

const Template = (args) => <Button {...args} />; がよくわからなかったので調べました

まずそれぞれの関係性の把握から

StorybookとTemplate関数、そしてButtonコンポーネントの関係を示す図

  1. Storybookは、異なる状態を表現するためのargsをTemplate関数に提供します。
  2. Template関数は、これらのargsをプロパティとしてButtonコンポーネントに渡します。
  3. Buttonコンポーネントは、特定のプロパティでレンダリングされ、その結果がStorybookで視覚的に表現されます。

ここの関係性のイメージを持っていないと、コードを読んでいても、今ひとつ腑に落ちなかった私です。

コードでの例として

Buttonコンポーネントcolorsizeという2つのプロパティを持っているとします。Storybookを使用して、これらのプロパティの異なる組み合わせを視覚的に確認したいと思います。

// Buttonコンポーネントの定義
const Button = ({ color, size }) => <button style={{ color, fontSize: size }}>Click me</button>;

// Template関数の定義
const Template = (args) => <Button {...args} />;

// Storybookのストーリー
export const LargeBlueButton = Template.bind({});
LargeBlueButton.args = {
  color: 'blue',
  size: '20px'
};

export const SmallRedButton = Template.bind({});
SmallRedButton.args = {
  color: 'red',
  size: '10px'
};
  • LargeBlueButtonSmallRedButtonという2つのストーリーを作成しています。
    • それぞれのストーリーは、Template関数を使用して、Buttonコンポーネントの特定の状態を作成します。argsオブジェクトは、それぞれのストーリーで異なるプロパティをButtonコンポーネントに渡します。
      • この結果、StorybookではLargeBlueButtonストーリーでは大きな青いボタンが、SmallRedButtonストーリーでは小さな赤いボタンが表示されます。

Template.bind({})は何をしているのか?

Template.bind({})JavaScriptbindメソッドを使用しています。bindメソッドは、関数に特定のthis値と前置引数を設定した新しい関数を作成します。

Storybookのコンテキストでは、Template.bind({})は新しい関数を作成しますが、this値は空のオブジェクト({})に設定されます。これは、Storybookが各ストーリーに対して新しい関数インスタンスを作成することを保証します。これにより、各ストーリーが独立して動作し、他のストーリーに影響を与えることなくargsを変更できます。

const Template = (args) => <Button {...args} />;

export const LargeBlueButton = Template.bind({});
LargeBlueButton.args = {
  color: 'blue',
  size: '20px'
};
  1. Template.bind({})を呼び出すと、Template関数の新しいインスタンスが作成されます。
  2. この新しい関数は、Template関数と全く同じ動作をしますが、this値が空のオブジェクト({})に設定されています。
  3. この新しい関数にargsプロパティを設定すると、それらのargsは新しい関数の呼び出しに使用されます。

独立したTemplateを生成して、argsの内容も変えられるようにするためにあるようです。

参考

ゼロから始めるStorybook入門(React編)