LoginSignup
1
0

More than 1 year has passed since last update.

React / TODOアプリ / コンポーネント化

Last updated at Posted at 2022-01-23

コンポーネント化とは

機能ごとに分割して別ファイルにしておくことで、使い回せたり、状態の管理もしやすく拡張性があるので再利用も容易にできるというメリットがあります。

前提

  • CodeSandboxを使用

実装手順

実装順 ページ内リンク
1 コンポーネント用のディレクトリ作成
2 テキストボックス
3 未完了TODOリスト
4 完了TODOリスト

コンポーネント用のディレクトリ作成

  • srcディレクト配下にcomponentsディレクトリを作成
    • これから作成するコンポーネントファイルは、このディレクトリに格納します。

テキストボックス

まずは、タスクの入力欄であるテキストボックスからコンポーネント化していきます。

src/components/InputTodo.jsx
import React from "react";

export const InputTodo = (props) => {
  const { todoText, onChange, onClick } = props;
  return (
    <div className="input-area">
      <input placeholder="TODOを入力" value={todoText} onChange={onChange} />
      <button onClick={onClick}>追加</button>
    </div>
  );
};
  • テキストボックス用のコンポーネントファイル(InpotTodo.jsx)を作成し、src/App.jsxからテキストボックス部分のコードをコピ-し、貼り付けてから修正を行なっています。
export const InputTodo = (props) => {
  const { todoText, onChange, onClick } = props;
  • 親コンポーネント(src/App.jsx)に記述のある関数をpropsを用いて受け取っています。
    • 分割代入を使用することで、受け取ったpropsの先頭にprops.と書く必要がなくなるため、記述量が減り、コードの見通しが良くなります。
<button onClick={onClick}>追加</button>

// 分割代入せずに使用する場合
<button onClick={props.onClick}>追加</button>
  • propsで受け取った関数を使用しています。
src/App.jsx
import React, { useState } from "react";
import "./styles.css";
import { InputTodo } from "./components/InputTodo";

export const App = () => {
  const [todoText, setTodoText] = useState("");

  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeTodoText = (event) => setTodoText(event.target.value);

  const onClickAdd = () => {
    if (todoText === "") return;
    const newTodos = [...incompleteTodos, todoText];
    setIncompleteTodos(newTodos);
    setTodoText("");
  };
  .
  .
  .
  return (
    <>
      <InputTodo
        todoText={todoText}
        onChange={onChangeTodoText}
        onClick={onClickAdd}
      />
      .
      .
      .

次に親コンポーネントであるApp.jsxファイルを編集します。

import { InputTodo } from "./components/InputTodo";
  • コンポーネント化したInputTodoファイルのインポートを宣言します。
<InputTodo
  todoText={todoText}
  onChange={onChangeTodoText}
  onClick={onClickAdd}
/>
  • 先ほどコピーしたテキストボックス部分のコードを削除し、<InputTodo />に置き換えることでコンポーネントを呼び出すことができます。
  • コンポーネント内には、Input.jsxに関数を渡すための記述をしています。
    • porps(任意の名前)に関数を代入しています -> props名={関数名}
      • JavaScriptを渡すのでブランケット{}で関数を囲っています。

未完了リスト

次に未完了TODOリスト欄をコンポーネント化していきます。

src/components/IncompleteTodos.jsx
import React from "react";

export const IncompleteTodos = (props) => {
  const { todos, onClickComplete, onClickDelete } = props;
  return (
    <div className="incomplete-area">
      <p className="title">未完了のTODO</p>
      <ul>
        {todos.map((todo, index) => {
          return (
            <div key={todo} className="list-row">
              <li>{todo}</li>
              <button onClick={() => onClickComplete(index)}>完了</button>
              <button onClick={() => onClickDelete(index)}>削除</button>
            </div>
          );
        })}
      </ul>
    </div>
  );
};
src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos"; // 追加

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

  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    newIncompleteTodos.splice(index, 1);
    setIncompleteTodos(newIncompleteTodos);

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setCompleteTodos(newCompleteTodos);
  };
  .
  .
  .
  return (
    <>
      .
      .
      .
      <IncompleteTodos
        todos={incompleteTodos}
        onClickComplete={onClickComplete}
        onClickDelete={onClickDelete}
      />

先ほどと同様の実装のため、解説は割愛します。

完了リスト

最後に完了TODOリスト欄をコンポーネント化していきます。

src/components/IncompleteTodos.jsx
import React from "react";

export const CompleteTodos = (props) => {
  const { todos, onClickBack } = props;
  return (
    <div className="complete-area">
      <p className="title">完了のTODO</p>
      <ul>
        {todos.map((todo, index) => {
          return (
            <div key={todo} className="list-row">
              <li>{todo}</li>
              <button onClick={() => onClickBack(index)}>戻る</button>
            </div>
          );
        })}
      </ul>
    </div>
  );
};
src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos";
import { CompleteTodos } from "./components/CompleteTodos"; // 追加

export const App = () => {
  .
  .
  .
  const [completeTodos, setCompleteTodos] = useState([]);
  .
  .
  .
  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);
    setCompleteTodos(newCompleteTodos);
  .
  .
  .
  return (
    <>
      .
      .
      .
      <CompleteTodos todos={completeTodos} onClickBack={onClickBack} />

先ほどと同様の実装のため、解説は割愛します。

完成形のソースコード

src/App.jsx
import React, { useState } from "react";
import "./styles.css";

import { InputTodo } from "./components/InputTodo";
import { IncompleteTodos } from "./components/IncompleteTodos";
import { CompleteTodos } from "./components/CompleteTodos";

export const App = () => {
  const [todoText, setTodoText] = useState("");

  const [incompleteTodos, setIncompleteTodos] = useState([]);
  const [completeTodos, setCompleteTodos] = useState([]);

  const onChangeTodoText = (event) => setTodoText(event.target.value);

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

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

  const onClickComplete = (index) => {
    const newIncompleteTodos = [...incompleteTodos];
    newIncompleteTodos.splice(index, 1);
    setIncompleteTodos(newIncompleteTodos);

    const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
    setCompleteTodos(newCompleteTodos);
  };

  const onClickBack = (index) => {
    const newCompleteTodos = [...completeTodos];
    newCompleteTodos.splice(index, 1);
    setCompleteTodos(newCompleteTodos);

    const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
    setIncompleteTodos(newIncompleteTodos);
  };
  return (
    <>
      <InputTodo
        todoText={todoText}
        onChange={onChangeTodoText}
        onClick={onClickAdd}
      />
      <IncompleteTodos
        todos={incompleteTodos}
        onClickComplete={onClickComplete}
        onClickDelete={onClickDelete}
      />
      <CompleteTodos todos={completeTodos} onClickBack={onClickBack} />
    </>
  );
};

このようにコンポーネント化したことでreturn();内のHTML部分がスッキリとしてコードの見通しが良くなったことが分かります。

最後に

実はじゃけぇさんが提供しているもう一本の教材も購入しています笑
中級者レベルと難易度も上がっているので、学習しながらステップアップしちゃいます!

参考教材

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0