プログラミング

【React入門】ToDoリストを作って基礎知識を学ぼう【簡単】

React入門でToDoリストを作ってみよう.png

この記事では、これからReactに入門したいという方に向けた内容を書いていきます。

内容としては、Todoリストを実際にReactで作成していくというものになります。

この記事を通して、
・React開発の基礎
を身に付けられる内容となってます。

ぜひ、Todoリストを作成してReactに入門してください。

Reactエンジニアを目指している方へ、

知識0からReactエンジニアになるまでのロードマップをまとめました。
こちらも記事もぜひ参考にどうぞ。

Reactロードマップ
【完全無料】Reactエンジニアへなるための学習ロードマップ【高年収】Reactを学びたいと思ってますか?Reactエンジニアから勉強方法を学ぶのが一番効率的です。なので今回は、現役のReactエンジニアがReactの学習ロードマップを無料で公開します。Reactは今とても需要が高いスキルなので、今すぐ学びましょう。...

スクールでReactを深く学びたい方へ、

Reactを学べるおすすめのプログラミングスクールもまとめてますので、こちらもどうぞ。

reactを学べるプログラミングスクール
【最新】Reactを学べるおすすめプログラミングスクール3選Reactを学びたいですか?プログラミング学習で最も効率的なのはプログラミングスクールに通うことです。なので今回は、おすすめのプログラミングスクールを厳選しました。これでスクール選びで失敗することは無いです。Reactは需要が高いスキルなので、今すぐ学びましょう。...

Reactのおすすめ教材が知りたい方へ、

現役のReactエンジニアが>Reactのおすすめ教材をまとめました。
こちらも記事もぜひ参考にどうぞ。

【入門】React初心者が0から知識を身につける勉強法・教材のまとめ「React初心者だけど、勉強して入門したい」と思っている方必見。記事では、React初心者に向けて入門レベルからおすすめの学習教材を紹介しています。Reactエンジニアである僕が、勉強法なども合わせて解説しているのかなり参考になるかと。...
本記事の著者
hinoshin
ひのしん
現役フリーランスエンジニア
  • 文系学部(体育学部)卒だけど、Webエンジニアに転職成功
  • 現役のReactエンジニア
  • 実務10ヶ月でフリーランスとして独立
  • 元々勉強が苦手で、大学の偏差値は50ほど
  • 血液型はO型、千葉県出身の神奈川県在住、8月生まれの現在26歳

1なぜTodoリストなのか?

今回作成するアプリがなぜTodoリストなのか?

ということですが、Todoリストは基本的なシステムの主機能である、CRUDを有しているからです。

CRUDとは、

  • Create 作成
  • Read 編集
  • Update 更新
  • Delete 削除

です。

つまり、Todoリストが作れれば基礎はできるようになったということになります。

なので、今回はTodoリストという題材を選択しました。

2Reactの環境構築をしよう

まずは、環境構築をやりますが、いつも通りやるだけです。

nodeをインストールしたらcreate-react-appをしてアプリを作成します。(わからない人はググってください)

ここからはApp.jsのみを編集していきます。

まずは、一番外のdivタグ以外を全て削除してください。
後は、一番外のクラス名をapp-headerに変更すれば完了です。

ヘッダーではないので本来であれば不適切ですが、cssはいじりたくないですし、なんとなくこの背景色の方がカッコいいので 笑

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App-header">
    </div>
  );
}

export default App;

ローカルサーバーを立ち上げて、Reactの初期画面と同じ黒背景が表示されていれば環境構築は完了です。

また今回は最後まで、cssファイルは触らないのでそのままで大丈夫です。

3JSXでモックを作成しよう

まずは、stateやpropsを持たない静的なモックを作っていきます。

最初にh1タグにTodoリストと入力します。

今回のアプリでは主に3つの領域があります。

  1. Todoの入力エリア
  2. 未完了のTodoエリア
  3. 完了のTodoエリア

です。

これらをdivタグで作っていきます。
まずはh2タイトルと、インプットエリア、追加ボタンを持った、Todoの入力エリアを作ります。

次に、h2タイトルと、未完了のTodoを持つulタグを作成します。
未完了のTodoには、完了ボタンと削除ボタンをそれぞれ持たせます。

最後に、h2のタイトルと、完了のTodoを持つulタグを作れば完成です。
完了のTodoには、戻すボタンを持たせます。

コンポーネントのソースは下記になります。

function App() {
  return (
    <div className="App-header">
      <h1>TODOリスト</h1>
      <div>
        <h2>入力エリア</h2>
        <input placeHolder="TODOを入力"/>
        <button>追加</button>
      </div>
      <div>
        <h2>未完了のTODO</h2>
        <ul>
          <li>TODO1</li>
          <button>完了</button>
          <button>削除</button>
          <li>TODO2</li>
          <button>完了</button>
          <button>削除</button>
        </ul>
      </div>
      <div>
        <h2>完了のTODO</h2>
          <ul>
            <li>TODO3</li>
            <button>戻す</button>
            <li>TODO4</li>
            <button>戻す</button>
          </ul>
        </div>
    </div>
  );
}

ちなみに、画面はこのようになっているかと。
default

次からはアプリに動的な機能を実装していきます。

4ToDoリストに追加機能を実装してみよう

インプットエリアに入力し、追加ボタンを押したものが未完了のTodoエリアに追加されていくような機能を実装していきます。

まずは、今アプリで使用するstateを一通り宣言していきます。

今回は、

  • 入力用にinputTodo
  • 未完了のTodo用にincompleteTodos
  • 完了のTodo用にcompleteTodos

という3つのstateを使います。

stateを用意したら、incompleteTodosとcompleteTodosという配列を、mapで回して表示するようにします。
このリストの要素にkeyを渡すのを忘れないようにしましょう。

次に、キーボードから入力したものがインプットエリアに表示されるようにしていきます。

これは、インプットエリアにonChangeイベントを持たせ、入力された値をsetInputTodoで変更させるようにすればOKです。

次に、追加ボタンが押された時の機能を実装していきます。

これはボタンが押された時に、inputTodoの値をincompleteTodosに追加するような機能を実装すれば完了です。

一応、空の状態でボタンが押された場合のことも考えて、空の場合はreturnするような実装にしておきましょう。
また、入力後はインプットエリアが空になるようにもしておきましょう。

現時点でのソースはこんな感じになります。

import './App.css';
import {useState} from "react";

function App() {
  const [inputTodo, setInputTodo] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState(["test1","test2"]);
  const [completeTodos, setCompleteTodos] = useState(["test3", "test4"]);

  const onChangeInputTodo = (e) => {
    setInputTodo(e.target.value);
  }

  const onClickAdd = () => {
    if(inputTodo === "") return;
    const newTodos = [...incompleteTodos, inputTodo];
    setIncompleteTodos(newTodos);
    setInputTodo("");
  }

  return (
    <div className="App-header">
      <h1>TODOリスト</h1>
      <div>
        <h2>入力エリア</h2>
        <input placeHolder="TODOを入力" value={inputTodo} onChange={onChangeInputTodo}/>
        <button onClick={onClickAdd}>追加</button>
      </div>
      <div>
        <h2>未完了のTODO</h2>
        <ul>
          {incompleteTodos.map((todo) => {
          return(
            <div key={todo}>
              <li>{todo}</li>
              <button>完了</button>
              <button>削除</button>
            </div>
          )
          })}
        </ul>
      </div>
      <div>
        <h2>完了のTODO</h2>
          <ul>
            {completeTodos.map((todo) => {
            return(
              <div key={todo}>
                <li>{todo}</li>
                <button>戻す</button>
              </div>
            )
            })}
          </ul>
        </div>
    </div>
  );
}

export default App;

これで、追加ボタンを押すと要素が追加されるようになっているかと思います。

5削除機能の実装もしてみよう

削除ボタンが押された時に、そのTodoが削除されるような機能を実装していきます。

これは、ボタンが押された時に押されたTodoを削除するようなクリックイベントを付与すれば完成します。

しかし、現時点では押されたTodoがどれなのかを判別する手段がありません。
なので、mapにindexを追加しましょう。

そして、クリックイベントにindexを引数として渡すようにしてやれば、配列の何番目の要素が押されたかを判別できます。

後は、そのindexから1つを削除するためにspliceを使い、新しく作成した配列をsetIncompleteTodosで設定してやれば完了です。

ソースは下記になります。

import './App.css';
import {useState} from "react";

function App() {
  const [inputTodo, setInputTodo] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState(["test1","test2"]);
  const [completeTodos, setCompleteTodos] = useState(["test3", "test4"]);

  const onChangeInputTodo = (e) => {
    setInputTodo(e.target.value);
  }

  const onClickAdd = () => {
    if(inputTodo === "") return;
    const newTodos = [...incompleteTodos, inputTodo];
    setIncompleteTodos(newTodos);
    setInputTodo("");
  }

  const onClickDelete = (i) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(i, 1);
    setIncompleteTodos(newTodos);
  }

  return (
    <div className="App-header">
      <h1>TODOリスト</h1>
      <div>
        <h2>入力エリア</h2>
        <input placeHolder="TODOを入力" value={inputTodo} onChange={onChangeInputTodo}/>
        <button onClick={onClickAdd}>追加</button>
      </div>
      <div>
        <h2>未完了のTODO</h2>
        <ul>
          {incompleteTodos.map((todo, index) => {
          return(
            <div key={todo}>
              <li>{todo}</li>
              <button>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </div>
          )
          })}
        </ul>
      </div>
      <div>
        <h2>完了のTODO</h2>
          <ul>
            {completeTodos.map((todo) => {
            return(
              <div key={todo}>
                <li>{todo}</li>
                <button>戻す</button>
              </div>
            )
            })}
          </ul>
        </div>
    </div>
  );
}

export default App;

ちなみに、わざわざnewTodosという配列のコピーを作っているのはstateはset~以外で編集ができないからです。

6完了機能の実装もしてみよう

次は完了ボタンが押された時に、そのTodoを完了のTodoエリアに追加するような機能になります。

この関数にも引数としてindexを渡します。

そして、incompleteTodosからそのindex番目の要素を削除します。
後は、削除した要素をcompleteTodosに追加すればOKです。

コードで表現すると以下のようになります。

 const onClickComplete = (i) => {
    const newIncompleteTodos = [...incompleteTodos];
    const newCompleteTodos = [...completeTodos, incompleteTodos[i]];

    newIncompleteTodos.splice(i, 1);

    setCompleteTodos(newCompleteTodos)
    setIncompleteTodos(newIncompleteTodos);
  }

この関数を完了ボタンに持たせてあげれば完成です。

7最後に戻す機能の実装しよう

最後の機能として、押された時に完了のTodoから未完了のTodoを移動させる、戻すボタンを実装します。

これも完了ボタンとやることは一緒です。

まず、indexを引数で渡してやり、受け取ったindex番目の要素をcompleteTodosから削除します。

そして、その削除した要素をincompleteTodosに追加してやれば終わりです。

const onClickReturn = (i) => {
    const newIncompleteTodos = [...incompleteTodos, completeTodos[i]];
    const newCompleteTodos = [...completeTodos];

    newCompleteTodos.splice(i, 1);

    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  }

後は、デフォルトで持たせていたincompleteTodosとcompleteTodosの要素を空にすればアプリ自体も完成です。
完成時のコードはこちらになります。

import './App.css';
import {useState} from "react";

function App() {
  const [inputTodo, setInputTodo] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeInputTodo = (e) => {
    setInputTodo(e.target.value);
  }

  const onClickAdd = () => {
    if(inputTodo === "") return;
    const newTodos = [...incompleteTodos, inputTodo];
    setIncompleteTodos(newTodos);
    setInputTodo("");
  }

  const onClickDelete = (i) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(i, 1);
    setIncompleteTodos(newTodos);
  }

  const onClickComplete = (i) => {
    const newIncompleteTodos = [...incompleteTodos];
    const newCompleteTodos = [...completeTodos, incompleteTodos[i]];

    newIncompleteTodos.splice(i, 1);

    setCompleteTodos(newCompleteTodos)
    setIncompleteTodos(newIncompleteTodos);
  }

  const onClickReturn = (i) => {
    const newIncompleteTodos = [...incompleteTodos, completeTodos[i]];
    const newCompleteTodos = [...completeTodos];

    newCompleteTodos.splice(i, 1);

    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  }

  return (
    <div className="App-header">
      <h1>TODOリスト</h1>
      <div>
        <h2>入力エリア</h2>
        <input placeHolder="TODOを入力" value={inputTodo} onChange={onChangeInputTodo}/>
        <button onClick={onClickAdd}>追加</button>
      </div>
      <div>
        <h2>未完了のTODO</h2>
        <ul>
          {incompleteTodos.map((todo, index) => {
          return(
            <div key={todo}>
              <li>{todo}</li>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </div>
          )
          })}
        </ul>
      </div>
      <div>
        <h2>完了のTODO</h2>
          <ul>
            {completeTodos.map((todo, index) => {
            return(
              <div key={todo}>
                <li>{todo}</li>
                <button onClick={() => onClickReturn(index)}>戻す</button>
              </div>
            )
            })}
          </ul>
        </div>
    </div>
  );
}

export default App;

8追加でコンポーネント化をしてみよう

アプリ自体は完成したのですが、全て1ファイルにコードを書いているため、見通したが悪いですし、Reactっぽく無いです。

なので、いくつかの要素をコンポーネント化していきましょう。

コンポーネント化することで、コードが見通しがよくなりまし、コンポーネントの再利用などが可能になります。
今回は再利用する要素はありませんが。

さて、コンポーネント化をしていくのですが、分け方は最初にdivタグで作った

  • インプットエリア
  • 未完了のTodoエリア
  • 完了のTodoエリア

をそれぞれコンポーネント化していきます。

まずは、srcフォルダ配下にcomponentsフォルダを作ります。

インプットエリアのコンポーネント化

最初に、インプットエリアをコンポーネント化していくので、InputTodo.jsxというファイルを作成します。

そして、対象のdivタグを持ってきてexportしてやるだけです。

後は、ここで使用している、inputTodo、onChangeInputTodo、onClickAddをpropsで渡してやります。

コードはこちら

export const InputTodo = (props) => {
  const {inputTodo, onChangeInputTodo, onClickAdd} = props;
  return(
    <div>
        <h2>入力エリア</h2>
        <input placeHolder="TODOを入力" value={inputTodo} onChange={onChangeInputTodo}/>
        <button onClick={onClickAdd}>追加</button>
      </div>
  );
}

未完了のTodoエリアのコンポーネント化

次に未完了のTodoエリアです。

同じようにcomponents配下にIncompleteTodo.jsxを作成して、対象のdivタグをコピーします。

また、使っているstateや関数をpropsとして渡してやります。

export const IncompleteTodos = (props) => {
  const {incompleteTodos, onClickComplete, onClickDelete} = props;
  return(
    <div>
        <h2>未完了のTODO</h2>
        <ul>
          {incompleteTodos.map((todo, index) => {
          return(
            <div key={todo}>
              <li>{todo}</li>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </div>
          )
          })}
        </ul>
      </div>
  );
}

完了のTodoエリアのコンポーネント化

同じようにCompleteTodos.jsxを作成すればOkです。

export const CompleteTodos = (props) => {
  const {completeTodos, onClickReturn} = props;
  return(
    <div style={{"backgroundColor" : "blue", "marginTop" : "18px", "marginBottom" : "50px" ,"padding" : "0px 18px"}}>
        <h2>完了のTODO</h2>
          <ul>
            {completeTodos.map((todo, index) => {
            return(
              <div key={todo}>
                <li>{todo}</li>
                <button onClick={() => onClickReturn(index)}>戻す</button>
              </div>
            )
            })}
          </ul>
        </div>
  );
}

後は、App.jsの方でこれらをimportすれば完成です。

import './App.css';
import {useState} from "react";
import {InputTodo} from "./components/InputTodo";
import { IncompleteTodos } from './components/IncompleteTodos';
import { CompleteTodos } from './components/CompleteTodos';

function App() {
  const [inputTodo, setInputTodo] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeInputTodo = (e) => {
    setInputTodo(e.target.value);
  }

  const onClickAdd = () => {
    if(inputTodo === "") return;
    const newTodos = [...incompleteTodos, inputTodo];
    setIncompleteTodos(newTodos);
    setInputTodo("");
  }

  const onClickDelete = (i) => {
    const newTodos = [...incompleteTodos];
    newTodos.splice(i, 1);
    setIncompleteTodos(newTodos);
  }

  const onClickComplete = (i) => {
    const newIncompleteTodos = [...incompleteTodos];
    const newCompleteTodos = [...completeTodos, incompleteTodos[i]];

    newIncompleteTodos.splice(i, 1);

    setCompleteTodos(newCompleteTodos)
    setIncompleteTodos(newIncompleteTodos);
  }

  const onClickReturn = (i) => {
    const newIncompleteTodos = [...incompleteTodos, completeTodos[i]];
    const newCompleteTodos = [...completeTodos];

    newCompleteTodos.splice(i, 1);

    setIncompleteTodos(newIncompleteTodos);
    setCompleteTodos(newCompleteTodos);
  }

  return (
    <div className="App-header">
      <h1>TODOリスト</h1>
      <InputTodo inputTodo={inputTodo} onChangeInputTodo={onChangeInputTodo} onClickAdd={onClickAdd}/>
      <IncompleteTodos incompleteTodos={incompleteTodos} onClickComplete={onClickComplete} onClickDelete={onClickDelete} />
      <CompleteTodos completeTodos={completeTodos} onClickReturn={onClickReturn}/>
    </div>
  );
}

export default App;

おさらいしておきますと、ファイル構成は以下のようになります。

src
-components
–InputTodo.jsx
–IncompleteTodo.jsx
–CompleteTodo.jsx
-App.js

9CSS in JSとは【必須知識】

最後におまけの勉強として、jsxファイル内でcssを指定する方法を紹介します。

まずは、cssを設定したいタグを決め、そこにstyle属性を追加します。

そこに、jsのオブジェクトしてcssのセレクタとプロパティを設定するだです。

実際に見ていきましょう。

export const InputTodo = (props) => {
  const {inputTodo, onChangeInputTodo, onClickAdd} = props;
  return(
    <div style={{"border": "1px solid white", "padding" : "9px 36px"}}>
export const IncompleteTodos = (props) => {
  const {incompleteTodos, onClickComplete, onClickDelete} = props;
  return(
    <div style={{"backgroundColor" : "lightblue", "marginTop" : "18px", "padding" : "0px 18px"}}>
export const CompleteTodos = (props) => {
  const {completeTodos, onClickReturn} = props;
  return(
    <div style={{"backgroundColor" : "blue", "marginTop" : "18px", "marginBottom" : "50px" ,"padding" : "0px 18px"}}>

正直ダサイですが、cssの書き方さえ学べれば良いと思うので、お許しください。。

css指定後の画面はこのようになっているかと思います。
todo-css

【React入門】ToDoリストを作って基礎知識を学ぼう まとめ

今記事では、Reactを使ってTodoリストを作ってきました。

コードは全てこちらにもありますので、参考にどうぞ。
https://github.com/hinoharashinya/react-todolist

このように、何かを学んだ後は実際に何か作ってみるという勉強法が最強だと思いますので、色々作ってどんどん知識を深めていきましょう。

また、リファクタリングしたり、追加機能を実装したりするのも良い勉強になると思うので、やってみてください。

僕のブログでは他にもReactのチュートリアルやプログラミングについての記事を書いてますので、ぜひ参考にしてみてください。

Reactエンジニアになりたい人はこちらからどうぞ

Reactロードマップ
【完全無料】Reactエンジニアへなるための学習ロードマップ【高年収】Reactを学びたいと思ってますか?Reactエンジニアから勉強方法を学ぶのが一番効率的です。なので今回は、現役のReactエンジニアがReactの学習ロードマップを無料で公開します。Reactは今とても需要が高いスキルなので、今すぐ学びましょう。...

Reactを学べるおすすめのプログラミングスクールを知りたい方はこの記事をどうぞ。

reactを学べるプログラミングスクール
【最新】Reactを学べるおすすめプログラミングスクール3選Reactを学びたいですか?プログラミング学習で最も効率的なのはプログラミングスクールに通うことです。なので今回は、おすすめのプログラミングスクールを厳選しました。これでスクール選びで失敗することは無いです。Reactは需要が高いスキルなので、今すぐ学びましょう。...

Reactのおすすめ教材を知りたい人はこの記事をどうぞ。

【入門】React初心者が0から知識を身につける勉強法・教材のまとめ「React初心者だけど、勉強して入門したい」と思っている方必見。記事では、React初心者に向けて入門レベルからおすすめの学習教材を紹介しています。Reactエンジニアである僕が、勉強法なども合わせて解説しているのかなり参考になるかと。...
ABOUT ME
ひのしん
【執筆者情報】ひの腹です。「正しい努力をすれば誰でも成果は出せる」 をモットーに発信しています。元ニートのフリーランスエンジニア。0スキルアップに関する知識を発信してます。分かりやすさ、効率の良さ、簡単さを意識して記事を書いています。
【次世代型】サブスクのプログラミングスクール

「プログラミングスクールが高くて通えない。。」

といった悩みがこのサービスで解決します。

それは、次世代型のサブスクプログラミングスクールになります。

具体的に、このスクールは以下のことが可能です。

  • 講師とのマンツーマンレッスン
  • 質の高いかなりボリュームのある教材
  • 講師に質問し放題

そして、お値段はたったの1,980円から。

これで高いお金を払わずに、エンジニアになることが可能です。

今なら、全額返金保証もあります。

エンジニアを目指す人も年々増えているで、お早めにどうぞ。

当サイト限定の、初月50%OFFクーポン(HINOSHIN)あり

>>侍テラコヤの評判・口コミ|現役エンジニアが実際に使ってみた感想

\全額返金保証/
侍テラコヤを詳しく見る