WEBアプリのフロントエンドに触りたいと思い、ToDoアプリを作成してみました。
Reactの基本的な用語や環境構築について記載しているため、初学者の方でもわかりやすい内容かと思います。
タイトルにもありますが、ToDoアプリを作成します。
タスクはチェックで完了/未完了を判別でき、フィルターで
絞り込むことができます。

Node.js:JavaScriptの実行環境で、サーバーサイドまで含めたアプリケーションの開発が可能となります
npm:JavaScriptのパッケージ管理ツールです
以下のサイトから自分のPCのOSにあった推奨版インストーラーを選択します。
※ダウンロードサイト
Node.jsダウンロードサイト
Macの場合は、Windowsの場合はコマンドプロンプトを開いて、以下のコマンドを実行します。
node --version |
正常にインストールされていれば、以下のようにバージョンが表示されます。
v14.17.2 |
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 |
アプリを作成する上で基本用語について説明していきます。
親コンポーネントから子コンポーネントへ値を渡すためのデータをプロップスと言います。
子コンポーネントにpropsとしてデータを渡すことで、ToDoが表示されます。

※参考
コンポーネントと props
ユーザーの動作の合わせて変わる値のことをstateと言います。
コンポーネントの内部で宣言・制御されるデータです。
propsとstateの主な違いは以下になります。
・propsは不変のデータだが、stateは可変のデータ
・propsは親コンポーネントから渡されるデータ、コンポーネント内部で保持されるデータ
src直下にcomponentsフォルダを作成します。
このフォルダにコンポーネントを配置していきます。

各コンポーネントのコードを書いていきます。
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.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.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.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; |
コンポーネントをまとめて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'; |
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学習の手助けになれば幸いです。