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学習の手助けになれば幸いです。


