はじめに

WEBアプリのフロントエンドに触りたいと思い、ToDoアプリを作成してみました。
Reactの基本的な用語や環境構築について記載しているため、初学者の方でもわかりやすい内容かと思います。

アプリケーションの概要

タイトルにもありますが、ToDoアプリを作成します。
タスクはチェックで完了/未完了を判別でき、フィルターで
絞り込むことができます。

環境構築

npm/Node.jsのインストール

Node.js:JavaScriptの実行環境で、サーバーサイドまで含めたアプリケーションの開発が可能となります
npm:JavaScriptのパッケージ管理ツールです

インストール手順

以下のサイトから自分のPCのOSにあった推奨版インストーラーを選択します。

※ダウンロードサイト
Node.jsダウンロードサイト

動作確認

Macの場合は、Windowsの場合はコマンドプロンプトを開いて、以下のコマンドを実行します。

node --version

正常にインストールされていれば、以下のようにバージョンが表示されます。

v14.17.2

Bulma、classNamesのインストール

Bulmaとは無料で提供されているCSSフレームワークです。
classNamesはクラスの振り分けを便利にしてくれるnpmパッケージです。

以下のコマンドを実行します。

npm install bulma
npm install classnames

開発プロジェクトの作成

npm/Node.jsが正常にインストールできたら、開発プロジェクトを作成します。

ターミナル(コマンドプロンプト)で、プロジェクトを作成したい場所に移動して、以下のコマンドを実行します。

npx create-react-app todo

正常に実行されると、todoフォルダと配下に様々なファイルが作成されます。

また、ターミナル(コマンドプロンプト)に以下のような表示がされています。

作成したフォルダに移動し、以下のコマンドを入力します。

npm start

ブラウザに以下の画面が出れば成功です。

  • 以下のエラーが表示される場合

You are running create-react-app 4.0.3, which is behind the latest release (5.0.0).

以下のコマンドを実行

npx clear-npx-cache

Reactの基本用語

アプリを作成する上で基本用語について説明していきます。

component(コンポーネント)

見た目と機能を持つ部品のことをコンポーネントと言います。
今回作成するアプリでは、4つのコンポーネントで構成されています。

props(プロップス)

親コンポーネントから子コンポーネントへ値を渡すためのデータをプロップスと言います。
子コンポーネントにpropsとしてデータを渡すことで、ToDoが表示されます。

state(ステート)

ユーザーの動作の合わせて変わる値のことをstateと言います。
コンポーネントの内部で宣言・制御されるデータです。

propsとstateの主な違いは以下になります。

・propsは不変のデータだが、stateは可変のデータ
・propsは親コンポーネントから渡されるデータ、コンポーネント内部で保持されるデータ

アプリケーションの作成

componentsフォルダの作成

src直下にcomponentsフォルダを作成します。
このフォルダにコンポーネントを配置していきます。

コンポーネントの作成

InputToDoの作成

各コンポーネントのコードを書いていきます。
InputToDo.jsxに以下のコードを記述(貼り付け)します。

InputToDo.jsxでは入力欄とEnter押下時の動作を記載しています。
空白文字が入力された場合は、ToDoを追加せずに終了します。

import React, { useState } from 'react';
import 'bulma/css/bulma.css';
export const InputToDo = (props) => {
// stateを作成
const [text, setText] = useState('');
//入力値をtextに反映
const handleChange = e => setText(e.target.value);
// Enter押下時、ToDoに追加
const handleEnter = e => {
if (e.key === 'Enter') {
// 入力値が空白文字の場合終了
if (!text.match(/\S/g) ) return;
// ToDoAppクラスの「handleAdd」関数を実行
props.onAdd(text);
setText('');
}
};
return (
<div className="panel-block">
<input
class="input"
type="text"
placeholder="Enter to add"
value={text}
onChange={handleChange}
onKeyPress={handleEnter}
/>
</div>
);
}
export default InputToDo;

Filterの作成

Filter.jsxに以下のコードを記述(貼り付け)します。

Filter.jsxではフィルターを押下した時の動作を記載しています。

import classNames from 'classnames';
import 'bulma/css/bulma.css';
export const Filter = (props) => {
// propsを定義
const { value, onChange } = props;
// フィルターの切り替え
const handleClick = (key, event) => {
event.preventDefault();
onChange(key);
};
return (
<div className="panel-tabs">
<a
href="#"
onClick={handleClick.bind(null, 'ALL')}
className={classNames({ 'is-active': value === 'ALL' })}
>All</a>
<a
href="#"
onClick={handleClick.bind(null, 'TODO')}
className={classNames({ 'is-active': value === 'TODO' })}
>ToDo</a>
<a
href="#"
onClick={handleClick.bind(null, 'DONE')}
className={classNames({ 'is-active': value === 'DONE' })}
>Done</a>
</div>
);
}
export default Filter;

ToDoの作成

ToDo.jsxに以下のコードを記述(貼り付け)します。

ToDo.jsxでは入力されたToDoをチェックボックスと共に表示しています。
チェックボックスが押下された時は、propsのonCheck関数を呼び出しています。

import classNames from 'classnames';
import 'bulma/css/bulma.css';
export const ToDo = (props) => {
// stateを作成
const { todo, onCheck } = props;
// チェックボックス押下時、ToDoAppクラスの「handleCheck」関数を実行
const handleChange = () => {
onCheck(todo);
};
return (
<label className="panel-block">
<input
type="checkbox"
checked={todo.done}
onChange={handleChange}
/>
<span
className={classNames({
'has-text-grey-light': todo.done
})}
>
{todo.text}
</span>
</label>
);
}
export default ToDo;

ToDoAppの作成

ToDoApp.jsxに以下のコードを記述(貼り付け)します。

ToDoApp.jsxでは各コンポーネントをimportして、フィルターの切り替えによるToDoの表示、チェックボックスの判定を
行っています。

import React, { useState } from 'react';
import 'bulma/css/bulma.css';
import { InputToDo, Filter, ToDo } from './index';
export const ToDoApp = () => {
// ランダムなキーを取得
const getKey = () => Math.random().toString(32).substring(2);
// stateを作成
const [todos, setToDos] = useState([]);
const [filter, setFilter] = useState('ALL');
// 入力値をtodos(配列)に設定
const handleAdd = text => {
setToDos([...todos, { key: getKey(), text, done: false }]);
};
// フィルターの切り替え
const handleFilterChange = value => setFilter(value);
// フィルターに応じたToDoを表示
const displayToDos = todos.filter(todo => {
if (filter === 'ALL') return true;
if (filter === 'TODO') return !todo.done;
if (filter === 'DONE') return todo.done;
});
// チェックボックス判定
const handleCheck = checked => {
// チェックがついたToDoの真偽値(done)を変更
const newToDos = todos.map(todo => {
if (todo.key === checked.key) {
todo.done = !todo.done;
}
return todo;
});
setToDos(newToDos);
};
return (
<div className="panel is-warning">
<div className="panel-heading">
ToDo
</div>
<InputToDo onAdd={handleAdd} />
<Filter
onChange={handleFilterChange}
value={filter}
/>
{displayToDos.map(todo => (
<ToDo
key={todo.key}
todo={todo}
onCheck={handleCheck}
/>
))}
<div className="panel-block">
{displayToDos.length} todos
</div>
</div>
);
}
export default ToDoApp;

index.jsxの作成

コンポーネントをまとめてimportできるようindex.jsxを作成します。
以下のコードを記述(貼り付け)します。

export {default as InputToDo} from './InputToDo'
export {default as Filter} from './Filter'
export {default as ToDo} from './ToDo'

importする際は、以下のようにindex.jsxから複数のコンポーネントをimportします。

import { InputToDo, Filter, ToDo } from './index';

App.jsの修正

src直下にあるApp.jsに以下のコードを記述(貼り付け)します。

作成したToDoApp.jsxを記載しています。

import ToDoApp from "./components/ToDoApp";
function App() {
return (
<div className="container is-fluid">
<ToDoApp />
</div>
);
}
export default App;

まとめ

備忘録を兼ねて、環境構築からToDoアプリの作成についてご紹介いたしました。
まだまだ説明不足な点があるかと思いますが、React学習の手助けになれば幸いです。