0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[React][L7] コンポーネン化する(Propsの利用)

Posted at

実施条件

[React][L6] リストの項目をボタンで削除・出力する2を理解していること

環境

MacBook Pro (2.3 GHz 8コアIntel Core i9)
macOS 14.0(23A344)
Homebrew 4.3.8
gh 2.52.0

ソースコードの前に

フォルダ構成

folda_structure
├── package.json
├── public/
│   └── index.html
├── src/
│   ├── components/ 
│   │   ├── InputTodo.jsx
│   │   ├── IncompleteTodos.jsx
│   │   └── CompleteTodos.jsx
│   ├── index.jsx
│   ├── Todo.jsx
│   └── styles.css
│  

index.html

index.html
<!DOCTYPE html>
<html lang="en">
  <head> </head>

  <body>
    <div id="root"></div>
  </body>
</html>

index.jsx

index.jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import { Todo } from "./Todo";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <Todo />
  </StrictMode>
);

ソースコード

Todo.jsx(旧)

Todo.jsx
import { useState } from "react";
import "./styles.css";

export const Todo = () => {
  const [todoText, setTodoText] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState([
    "TODOです1",
    "TODOです2",
  ]);
  const [completeTodos, setCompleteTodos] = useState([
    "TODOでした1",
    "TODOでした2",
  ]);

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

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

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

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

  return (
    <>
      <div className="input-area">
        <input
          placeholder="TODOを入力"
          value={todoText}
          onChange={onChangeTodoText}
        ></input>
        <button onClick={onClickAdd}>追加</button>
      </div>
      <div className="incomplete-area">
        <p className="title">未完了のTODO</p>
        <ul>    
          {incompleteTodos.map((todo, index) => {
            return (
              <li key={todo}>
                <div className="list-row">
                  <p>{todo}</p>
                  <button onClick={() => onClickComplete(index)}>完了</button>
                  <button onClick={() => onClickDelete(index)}>削除</button>
                </div>
              </li>
            );
          })}
        </ul>
      </div>
      <div className="complete-area">
        <p className="title">完了のTODO</p>
        <ul>
          {completeTodos.map((todo, index) => {
            return (
              <li>
                <div className="list-row">
                  <p>{todo}</p>
                  <button onClick={() => onClickBack(index)}>戻す</button>
                </div>
              </li>
            );
          })}
        </ul>
      </div>
    </>
  );
};

Todo.jsx

Todo.jsx
import { useState } from "react";
+import { CompleteTodos } from "./components/CompleteTodos";
+import { IncompleteTodos } from "./components/IncompleteTodos";
+import { InputTodo } from "./components/InputTodo";
import "./styles.css";

export const Todo = () => {
  const [todoText, setTodoText] = useState("");
  const [incompleteTodos, setIncompleteTodos] = useState([
    "TODOです1",
    "TODOです2",
  ]);
  const [completeTodos, setCompleteTodos] = useState([
    "TODOでした1",
    "TODOでした2",
  ]);

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

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

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

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

  return (
    <>
-     <div className="input-area">
-       <input
-         placeholder="TODOを入力"
-         value={todoText}
-         onChange={onChangeTodoText}
-       ></input>
-       <button onClick={onClickAdd}>追加</button>
-     </div>
+     <div className="input-area">
+       <input
+         placeholder="TODOを入力"
+         value={todoText}
+         onChange={onChangeTodoText}
+       ></input>
+       <button onClick={onClickAdd}>追加</button>
+     </div>
-     <div className="incomplete-area">
-       <p className="title">未完了のTODO</p>
-       <ul>    
-         {incompleteTodos.map((todo, index) => {
-           return (
-             <li key={todo}>
-               <div className="list-row">
-                 <p>{todo}</p>
-                 <button onClick={() => onClickComplete(index)}>完了</button>
-                 <button onClick={() => onClickDelete(index)}>削除</button>
-               </div>
-             </li>
-           );
-         })}
-       </ul>
-     </div>
+     <IncompleteTodos
+       todos={incompleteTodos}
+       onClickComplete={onClickComplete}
+       onClickDelete={onClickDelete}
+     /> 
      <div className="complete-area">
        <p className="title">完了のTODO</p>
        <ul>
          {completeTodos.map((todo, index) => {
            return (
              <li>
                <div className="list-row">
                  <p>{todo}</p>
                  <button onClick={() => onClickBack(index)}>戻す</button>
                </div>
              </li>
            );
          })}
        </ul>
      </div>
    </>
  );
};

解説

目的

  • return以下の各divタグ(input-area incomplete-area complete-area)をコンポーネント化する

コーディング

InputTodo.jsxのコンポーネント化

  1. componentsディレクトリと各コンポーネントを書くフォルダを作成する

    folda_structure
     ├── package.json
     ├── public/
     │   └── index.html
     ├── src/
    +│   ├── components/ 
    +│   │   └── InputTodo.jsx
     │   ├── index.jsx
     │   ├── Todo.jsx
     │   └── styles.css
    
  2. コンポーネントの雛形を用意する

    InputTodo.jsx
    export const InputTodo = () => {
      return (
      );
    };
    
  3. コンポーネント化したい機能を、元のファイルから切り取る

    Todo.jsx
      return (
        <>
    -     <div className="input-area">
    -       <input
    -         placeholder="TODOを入力"
    -         value={todoText}
    -         onChange={onChangeTodoText}
    -       ></input>
    -       <button onClick={onClickAdd}>追加</button>
    -     </div>
    
    InputTodo.jsx
    export const InputTodo = () => {
      return (
    +   <div className="input-area">
    +     <input
    +       placeholder="TODOを入力"
    +       value={todoText}
    +       onChange={onChangeTodoText}
    +     ></input>
    +     <button onClick={onClickAdd}>追加</button>
    +   </div>
      );
    };
    
  4. todoText(配列) onChangeTodoText(関数) onClickAdd(関数)がないためエラーとなる

  5. 上記の配列や関数をInputTodo.jsxPropsを受け取れる状態にする

    InputTodo.jsx
    -export const InputTodo = () => {
    +export const InputTodo = (props) => {
      return (
        <div className="input-area">
          <input
            placeholder="TODOを入力"
            value={todoText}
            onChange={onChangeTodoText}
          ></input>
          <button onClick={onClickAdd}>追加</button>
        </div>
      );
    };
    
  6. Todo.jsxPropsを送れる状態にする

    Todo.jsx
    import { useState } from "react";
    +import { InputTodo } from "./components/InputTodo";
    import "./styles.css";
    
    //省略//
    
      return (
        <>
    +     <InputTodo
    +     />
    
  7. Todo.jsxが送るPropsを設定する

    Todo.jsx
    import { useState } from "react";
    import { InputTodo } from "./components/InputTodo";
    import "./styles.css";
    
    //省略//
    
      return (
        <>
          <InputTodo
    +       todoText={todoText}
    +       onChange={onChangeTodoText}
    +       onClick={onClickAdd}
          />
    
  8. InputTodo.jsxが受け取るPropsを設定する

    InputTodo.jsx
    export const InputTodo = (props) => {
    + const { todoText, onChange, onClick } = prorps;
      return (
        <div className="input-area">
          <input
            placeholder="TODOを入力"
            value={todoText}
            onChange={onChangeTodoText}
          ></input>
          <button onClick={onClickAdd}>追加</button>
        </div>
      );
    };
    
  9. 受け取るPropsに合わせて変数名を修正する

    InputTodo.jsx
    export const InputTodo = (props) => {
    + const { todoText, onChange, onClick } = prorps;
      return (
        <div className="input-area">
          <input
            placeholder="TODOを入力"
            value={todoText}
    -       onChange={onChangeTodoText}
    +       onChange={onChange}        
          ></input>
    -     <button onClick={onClickAdd}>追加</button>
    +     <button onClick={onClick}>追加</button>
        </div>
      );
    };
    

IncompleteTodosのコンポーネント化

  1. componentsディレクトリと各コンポーネントを書くフォルダを作成する

    folda_structure
     ├── package.json
     ├── public/
     │   └── index.html
     ├── src/
     │   ├── components/ 
     │   │   ├── InputTodo.jsx
    +│   │   └── IncompleteTodos.jsx
     │   ├── index.jsx
     │   ├── Todo.jsx
     │   └── styles.css
    
  2. コンポーネントの雛形を用意する

    IncompleteTodos.jsx
    export const IncompleteTodos = () => {
      return (
      );
    };
    
  3. コンポーネント化したい機能を、元のファイルから切り取る

    Todo.jsx
      return (
      
      //省略//
        
    -     <div className="incomplete-area">
    -       <p className="title">未完了のTODO</p>
    -       <ul>
    -         {incompleteTodos.map((todo, index) => {
    -           return (
    -             <li key={todo}>
    -               <div className="list-row">
    -                 <p>{todo}</p>
    -                 <button onClick={() => onClickComplete(index)}>完了</button>
    -                 <button onClick={() => onClickDelete(index)}>削除</button>
    -               </div>
    -             </li>
    -           );
    -         })}
    -       </ul>
    -     </div>
    
    IncompleteTodos.jsx
    export const IncompleteTodos = () => {
      return (
    +   <div className="incomplete-area">
    +     <p className="title">未完了のTODO</p>
    +     <ul>
    +       {incompleteTodos.map((todo, index) => {
    +         return (
    +           <li key={todo}>
    +             <div className="list-row">
    +               <p>{todo}</p>
    +               <button onClick={() => onClickComplete(index)}>完了</button>
    +               <button onClick={() => onClickDelete(index)}>削除</button>
    +             </div>
    +           </li>
    +         );
    +       })}
    +     </ul>
    +   </div>
      );
    };
    
  4. incompleteTodos(配列) onClickComplete(関数) onClickDelete(関数)がないためエラーとなる

  5. 上記の配列や関数をIncompleteTodos.jsxPropsを受け取れる状態にする

    IncompleteTodos.jsx
    -export const IncompleteTodos = () => {
    +export const IncompleteTodos = (props) => {
      return (
        <div className="incomplete-area">
          <p className="title">未完了のTODO</p>
          <ul>
            {IncompleteTodos.map((todo, index) => {
              return (
                <li key={todo}>
                  <div className="list-row">
                    <p>{todo}</p>
                    <button onClick={() => onClickComplete(index)}>完了</button>
                    <button onClick={() => onClickDelete(index)}>削除</button>
                  </div>
                </li>
              );
            })}
          </ul>
        </div>
      );
    };
    
  6. Todo.jsxPropsを送れる状態にする

    Todo.jsx
    import { useState } from "react";
    import { InputTodo } from "./components/InputTodo";
    +import { IncompleteTodos } from "./components/InCompleteTodos";
    import "./styles.css";
      
    //省略//
        
      return (
      
      //省略//
        
    +     <IncompleteTodos 
    +     />
    
  7. Todo.jsxが送るPropsを設定する

    Todo.jsx
    import { useState } from "react";
    import { InputTodo } from "./components/InputTodo";
    +import { IncompleteTodos } from "./components/InCompleteTodos";
    import "./styles.css";
      
    //省略//
        
      return (
      
      //省略//
        
          <IncompleteTodos
    +       todos={incompleteTodos}
    +       onClickComplete={onClickComplete}
    +       onClickDelete={onClickDelete}
          />
    
  8. IncompleteTodos.jsxが受け取るPropsを設定する

    IncompleteTodos.jsx
    export const IncompleteTodos = (props) => {
    + const { todos, onClickComplete, onClickDelete } = props;
      return (
        <div className="incomplete-area">
          <p className="title">未完了のTODO</p>
          <ul>
            {incompleteTodos.map((todo, index) => {
              return (
                <li key={todo}>
                  <div className="list-row">
                    <p>{todo}</p>
                    <button onClick={() => onClickComplete(index)}>完了</button>
                    <button onClick={() => onClickDelete(index)}>削除</button>
                  </div>
                </li>
              );
            })}
          </ul>
        </div>
      );
    };
    
  9. 受け取るPropsに合わせて変数名を修正する

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

CompleteTodosのコンポーネント化

  1. componentsディレクトリと各コンポーネントを書くフォルダを作成する
    folda_structure
     ├── package.json
     ├── public/
     │   └── index.html
     ├── src/
     │   ├── components/ 
     │   │   ├── InputTodo.jsx
     │   │   ├── IncompleteTodos.jsx
    +│   │   └── CompleteTodos.jsx
     │   ├── index.jsx
     │   ├── Todo.jsx
     │   └── styles.css
    
  2. コンポーネントの雛形を用意する
    CompleteTodos.jsx
    export const CompleteTodos = () => {
      return (
      );
    };
    
  3. コンポーネント化したい機能を、元のファイルから切り取る
Todo.jsx
  return (
  
  //省略//
  
-     <div className="complete-area">
-       <p className="title">完了のTODO</p>
-       <ul>
-         {completeTodos.map((todo, index) => {
-           return (
-             <li>
-               <div className="list-row">
-                 <p>{todo}</p>
-                 <button onClick={() => onClickBack(index)}>戻す</button>
-               </div>
-             </li>
-           );
-         })}
-       </ul>
-     </div>
CompleteTodos.jsx
export const CompleteTodos = () => {
  return (
+   <div className="complete-area">
+     <p className="title">完了のTODO</p>
+     <ul>
+       {completeTodos.map((todo, index) => {
+         return (
+           <li>
+             <div className="list-row">
+               <p>{todo}</p>
+               <button onClick={() => onClickBack(index)}>戻す</button>
+             </div>
+           </li>
+         );
+       })}
+     </ul>
+   </div>
  );
};
  1. completeTodos(配列) onClickBack(関数)がないためエラーとなる
  2. 上記の配列や関数をCompleteTodos.jsxPropsを受け取れる状態にする
CompleteTodos.jsx
-export const CompleteTodos = () => {
+export const CompleteTodos = (props) => {
  return (
    <div className="complete-area">
      <p className="title">完了のTODO</p>
      <ul>
        {completeTodos.map((todo, index) => {
          return (
            <li>
              <div className="list-row">
                <p>{todo}</p>
                <button onClick={() => onClickBack(index)}>戻す</button>
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
  1. Todo.jsxPropsを送れる状態にする
Todo.jsx
import { useState } from "react";
import { InputTodo } from "./components/InputTodo";
+import { CompleteTodos } from "./components/CompleteTodos";
import { IncompleteTodos } from "./components2/IncompleteTodos";

//省略//

  return (
  
  //省略//

+     <CompleteTodos 
+     />  
  1. Todo.jsxが送るPropsを設定する
Todo.jsx
import { useState } from "react";
import { InputTodo } from "./components/InputTodo";
+import { CompleteTodos } from "./components/CompleteTodos";
import { IncompleteTodos } from "./components2/IncompleteTodos";

//省略//

  return (
  
  //省略//

      <CompleteTodos
+       todos={completeTodos} 
+       onClickBack={onClickBack} />
      />
  1. CompleteTodos.jsxが受け取るPropsを設定する
CompleteTodos.jsx
export const CompleteTodos = (props) => {
+ const { todos, onClickBack } = props;
  return (
    <div className="complete-area">
      <p className="title">完了のTODO</p>
      <ul>
        {completeTodos.map((todo, index) => {
          return (
            <li>
              <div className="list-row">
                <p>{todo}</p>
                <button onClick={() => onClickBack(index)}>戻す</button>
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
};
  1. 受け取るPropsに合わせて変数名を修正する
CompleteTodos.jsx
export const CompleteTodos = (props) => {
  const { todos, onClickBack } = props;
  return (
    <div className="complete-area">
      <p className="title">完了のTODO</p>
      <ul>
-       {completeTodos.map((todo, index) => {
+       {todos.map((todo, index) => {
          return (
            <li>
              <div className="list-row">
                <p>{todo}</p>
                <button onClick={() => onClickBack(index)}>戻す</button>
              </div>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

参考リンク

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?