2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Next.js/daisy UI】子コンポーネント内で状態管理して、送信でまとめて親に渡そう

Posted at

はじめに

前回の記事に続いて、
モーダル関連の記事になります。

問題

モーダル内にはデータベースへとデータを送付するための、処理を実装していました。
ですが、inputタグのvalueonChangeの値を親のonClick処理で送付するってことにハマりました。

解決方法

まずは、下記のコードです。

ModalAddFolder.tsx
"use client";
import React, { useState } from "react";

import { Button } from "@/app/components/common/Button";
import { ModalBase } from "@/app/components/common/ModalBase/ModalBase";

interface ModalAddFolderProps {
  // データ通信が完成したらこっちを使う
  /**
   * onSubmit: (name: string) => Promise<void>;
   */
  onClickAddFolder: (name: string) => void;
  onClose: () => void;
}
export const ModalAddFolder: React.FC<ModalAddFolderProps> = ({ onClose, onClickAddFolder }) => {
  const [name, setName] = useState("");

  const onClickAdd = () => {
    onClickAddFolder(name);
  };

  return (
    <ModalBase title="フォルダを追加" description="フォルダを追加してください">
      <div className="flex items-center gap-2">
        <input
          type="text"
          placeholder="フォルダ名"
          className="input input-bordered w-full"
          value={name}
          onChange={(e) => setName(e.target.value)}
        />
      </div>
      <div className="flex items-center gap-2">
        <Button text="キャンセル" className="btn-ghost" onClickButton={onClose} />
        <Button text="追加" className="btn-primary" onClickButton={onClickAdd} />
      </div>
    </ModalBase>
  );
};

下記のコードのように、まずはinputでstate管理したいところを保持します

  const [name, setName] = useState("");
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   <input
        type="text"
        placeholder="フォルダ名"
        className="input input-bordered w-full"
        value={name}
        onChange={(e) => setName(e.target.value)}
    />

次に、propsで受け取る予定のonClickAddFolderを使い、
すでにクリックしたときに発火される関数を用意しておきます。

  const onClickAdd = () => {
    onClickAddFolder(name);
  };
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    <Button text="追加" className="btn-primary" onClickButton={onClickAdd} />

親元では下記のように呼び出します。

sidebar.tsx
"use client";
import React from "react";
import { useParams, usePathname } from "next/navigation";

import { CirclePlus } from "lucide-react";

import { LinkList } from "@/app/components/layout/Link";

import { FaRegUser } from "react-icons/fa";
import { Button } from "@/app/components/common/Button";
import { useModal } from "@/app/lib/hooks/useModal";

interface SidebarProps {
  userName: string;
  contents: { text: string; id: string }[];
}

export const Sidebar = ({ userName, contents }: SidebarProps) => {
  const pathname = usePathname();
  const { userId } = useParams();
  const { openModal, closeModal, ModalRenderer } = useModal();

  // 追加する処理をここに書く
  const handleAddFolder = (name: string) => {
    console.log(name);
    closeModal();
  };

  const onClickAddFolderModalOpen = () => {
    openModal("addFolder", { onClickAddFolder: handleAddFolder });
  };

  const customContents = contents.map((content) => ({
    ...content,
    id: `${userId}/${content.id}/`,
  }));

  const pathSegments = pathname.split("/");
  const activeId = `${pathSegments[2]}/${pathSegments[3]}/`;

  return (
    <>
      <aside className="flex flex-col gap-4 min-h-[100vh] pb-6 bg-base-200">
        <div className="flex flex-col items-start gap-8 h-full">
          <div className="flex items-center gap-2 p-4 border-b border-base-300 w-full">
            <FaRegUser className="text-xl" />
            <a className="text-xl font-semibold" href={`/dashboard/${userId}`}>
              {userName}
            </a>
          </div>
          <div className="border-b border-base-300 pb-[32px] w-full">
            <h2 className="px-4 text-sm font-medium text-base-content/60 uppercase tracking-wider mb-2">フォルダ</h2>
            <LinkList contents={customContents} activeId={activeId} />
          </div>
        </div>
        <div className="p-4 border-t border-base-300">
          <Button
            text="新しいフォルダを作成"
            icon={<CirclePlus className="h-4 w-4 mr-2" />}
            onClickButton={onClickAddFolderModalOpen}
          />
        </div>
      </aside>
      <ModalRenderer />
    </>
  );
};

呼び出すための関数をこのように用意します。
処理内容はよしなに。

  // 追加する処理をここに書く
  const handleAddFolder = (name: string) => {
    console.log(name);
    closeModal();
  };

下記のように、handleAddFolderをpropsで渡します。

  const onClickAddFolderModalOpen = () => {
    openModal("addFolder", { onClickAddFolder: handleAddFolder });
  };

これにより、コールバックでボタンを押下した時の処理が走るってことですね。
わかりやすく書くと、

  • Modalのコンポーネントは、inputの値を管理する
  • 呼び出し元のコンポーネントやもっと高レイヤーのコンポーネントで操作処理を記載する

やっていることはこれですね。

おわりに

前回の記事にも書きましたが、
途中にuseModalというカスタムフックがあったため、
このような形になりました。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?