はじめに
前回の記事に続いて、
モーダル関連の記事になります。
問題
モーダル内にはデータベースへとデータを送付するための、処理を実装していました。
ですが、inputタグのvalue
とonChange
の値を親の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というカスタムフックがあったため、
このような形になりました。