Blog

お役立ち情報

システム開発

React DocsのQuick StartをTypeScriptで書き直してみた

投稿日:2023.02.01

概要

Reactの公式ドキュメントの新しいものがbeta版ですが公開されています。( https://beta.reactjs.org/ )

こちらは関数コンポーネントを学習するのにおすすめとなっております。(2023/02/01現在 英語版のみ。)

 

さて、
ReactとTypeScriptの学習も兼ねて、
Quick StartのThinking in React( https://beta.reactjs.org/learn/thinking-in-react )をTypeScriptに書き直してみました。

 

環境

WSL2(Ubuntu 20.04.5)

Node.js 16.14.0

React 18.2.0

Create React App 5.0.1

TypeScript 4.8.4

 

環境構築

$ npx create-react-app sample_app –template typescript

 

プロジェクトを実行

$ npm start

 

ソースコード

  • 各コンポーネントは「components」ディレクトリにファイルを作成した
  • propsの型定義は各コンポーネントファイルに記述した
  • 複数箇所で共通で使用している型定義は「types」ディレクトリにファイルを作成した

 

src/components/FilterableProductTable.tsx

import { useState } from ‘react’;

 

import { SearchBar } from ‘./SearchBar’;

import { ProductTable } from ‘./ProductTable’;

 

import type { Product } from ‘../types/ProductType’;

 

type Props = {

  products: Product[];

};

 

export const FilterableProductTable: React.FC<Props> = ({ products }) => {

  const [filterText, setFilterText] = useState(”);

  const [inStockOnly, setInStockOnly] = useState(false);

 

  return (

    <>

      <SearchBar

        filterText={filterText}

        inStockOnly={inStockOnly}

        onFilterTextChange={setFilterText}

        onInStockOnlyChange={setInStockOnly}

      />

      <ProductTable products={products} filterText={filterText} inStockOnly={inStockOnly} />

    </>

  );

};

 

 

src/components/ProductCategoryRow.tsx

type Props = {

  category: string;

};

 

export const ProductCategoryRow: React.FC<Props> = ({ category }) => {

  return (

    <tr>

      <th colSpan={2}>{category}</th>

    </tr>

  );

};

 

 

src/components/ProductRow.tsx

import type { Product } from ‘../types/ProductType’;

 

type Props = {

  product: Product;

};

 

export const ProductRow: React.FC<Props> = ({ product }) => {

  const name = product.stocked ? (

    product.name

  ) : (

    <span style={{ color: ‘red’ }}>{product.name}</span>

  );

 

  return (

    <tr>

      <td>{name}</td>

      <td>{product.price}</td>

    </tr>

  );

};

 

 

src/components/ProductTable.tsx

import { ProductCategoryRow } from ‘./ProductCategoryRow’;

import { ProductRow } from ‘./ProductRow’;

 

import type { Product } from ‘../types/ProductType’;

 

type Props = {

  products: Product[];

  filterText: string;

  inStockOnly: boolean;

};

 

export const ProductTable: React.FC<Props> = ({ products, filterText, inStockOnly }) => {

  const rows: JSX.Element[] = [];

  let lastCategory: string | null = null;

 

  products.forEach((product) => {

    if (product.name.toLowerCase().indexOf(filterText.toLowerCase()) === -1) {

      return;

    }

    if (inStockOnly && !product.stocked) {

      return;

    }

    if (product.category !== lastCategory) {

      rows.push(<ProductCategoryRow category={product.category} key={product.category} />);

    }

    rows.push(<ProductRow product={product} key={product.name} />);

    lastCategory = product.category;

  });

 

  return (

    <table>

      <thead>

        <tr>

          <th>Name</th>

          <th>Price</th>

        </tr>

      </thead>

      <tbody>{rows}</tbody>

    </table>

  );

};

 

 

src/components/SearchBar.tsx

import React from ‘react’;

 

type Props = {

  filterText: string;

  inStockOnly: boolean;

  onFilterTextChange: React.Dispatch<React.SetStateAction<string>>;

  onInStockOnlyChange: React.Dispatch<React.SetStateAction<boolean>>;

};

 

export const SearchBar: React.FC<Props> = ({

  filterText,

  inStockOnly,

  onFilterTextChange,

  onInStockOnlyChange,

}) => {

  return (

    <form>

      <input

        type=”text”

        value={filterText}

        placeholder=”Search…”

        onChange={(e) => onFilterTextChange(e.target.value)}

      />

      <label>

        <input

          type=”checkbox”

          checked={inStockOnly}

          onChange={(e) => onInStockOnlyChange(e.target.checked)}

        />

        Only show products in stock

      </label>

    </form>

  );

};

 

 

src/types/ProductType.ts

export type Product = {

  category: string;

  price: string;

  stocked: boolean;

  name: string;

};

 

 

src/App.tsx

import { FilterableProductTable } from ‘./components/FilterableProductTable’;

 

const PRODUCTS = [

  { category: ‘Fruits’, price: ‘$1’, stocked: true, name: ‘Apple’ },

  { category: ‘Fruits’, price: ‘$1’, stocked: true, name: ‘Dragonfruit’ },

  { category: ‘Fruits’, price: ‘$2’, stocked: false, name: ‘Passionfruit’ },

  { category: ‘Vegetables’, price: ‘$2’, stocked: true, name: ‘Spinach’ },

  { category: ‘Vegetables’, price: ‘$4’, stocked: false, name: ‘Pumpkin’ },

  { category: ‘Vegetables’, price: ‘$1’, stocked: true, name: ‘Peas’ },

];

 

export const App = () => {

  return (

    <>

      <FilterableProductTable products={PRODUCTS} />

    </>

  );

};

 

 

src/index.tsx

import React from ‘react’;

import ReactDOM from ‘react-dom/client’;

import ‘./index.css’;

import { App } from ‘./App’;

import reportWebVitals from ‘./reportWebVitals’;

 

const root = ReactDOM.createRoot(

  document.getElementById(‘root’) as HTMLElement

);

root.render(

  <React.StrictMode>

    <App />

  </React.StrictMode>

);

 

// If you want to start measuring performance in your app, pass a function

// to log results (for example: reportWebVitals(console.log))

// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals

reportWebVitals();

 

 

最後に

こういう簡単なものからReactとTypeScriptの組み合わせを学習していくのが
ハードルが低く、おすすめです。

 

参考

 

システム開発やデザインのお悩みなら
yepが解決します!

何も決まっていなくても、業務内容に当てはまらなくても、
私たちにお話を聞かせてください!

お気軽にご相談ください

お問い合わせはこちら

アイコン

この記事をシェアする

この記事をシェア

コピーしました

人気の記事

私たちにお話を聞かせてください!

お問い合わせはこちら

アイコン

yepのこれまでの実績をご紹介

実績はこちら

アイコン
スタッフイメージ
スタッフイメージ
スタッフイメージ
スタッフイメージ
スタッフイメージ
スタッフイメージ
Recruit

採用情報

私たちと一緒に
新しいモノを造りませんか?

採用情報