ダボハゼのブログhttps://dabohaze.siteゆるく適当にSun, 07 Apr 2024 07:53:04 +0000jahourly1https://dabohaze.site/wp-content/uploads/2019/01/cropped-icon_site-32x32.pngダボハゼのブログhttps://dabohaze.site3232 SWRで取得したdataをReact Hook FormのdefaultValuesにセットするhttps://dabohaze.site/swr-react-hook-form-default-value/Sun, 07 Apr 2024 07:53:02 +0000https://dabohaze.site/?p=3919SWRで取得したdataを、React Hook Form(以降はRHF)のdefaultValuesにセットしたいことがあると思います。
以下のような感じです。

"use client";
import { useForm } from "react-hook-form";
import useSWR from "swr";
export default function Home() { const fetcher = () => fetch("/user/1").then((res) => res.json()); const { data, isLoading } = useSWR( "/user/1", fetcher ); const { register } = useForm({ defaultValues: { name: data?.name, }, }); return <input {...register("name")} />;
}

しかし、このやり方だとdefaultValuesにうまくセットされません。
なぜなら、dataはフェッチしたデータが入るまではundefinedとなっているため、defaultValuesにはundefinedがセットされてしまうためです。
データがフェッチされても、defaultValuesは上書きされないんですね。

この現象は、useEffectを使用すれば回避できます。

"use client";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import useSWR from "swr";
export default function Home() { const fetcher = () => fetch("/user/1").then((res) => res.json()); const { data, isLoading } = useSWR( "/user/1", fetcher ); const { register, reset } = useForm(); useEffect(() => { if (isLoading) return; reset({ name: data?.name }); }, [data, isLoading, reset]); return <input {...register("name")} />;
}

useEffectの依存配列にSWRのdataを指定しています。
SWRでデータがフェッチできたタイミングでuseEffectが実行され、RHKのresetで初期値をセットされます。

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【React】RHKのresetで勘違いしていたことhttps://dabohaze.site/react-hook-form-reset-empty/Sun, 22 Oct 2023 06:52:41 +0000https://dabohaze.site/?p=3904React Hook Formのresetは「Formを空にする」という理解でいましたが勘違いしていました。

"use client";
import { useForm, SubmitHandler } from "react-hook-form";
export default function Home() { const { register, reset } = useForm({ defaultValues: { hoge: "test", }, }); return ( <> <input {...register("hoge")} /> <button onClick={() => { reset(); }} > リセット </button> </> );
}

上記は、defaultValuesでhogeの初期値に「test」を設定しています。
hogeの値を変更後、リセットボタンを押下するとhogeの値は空ではなく「test」になりました。

値を空にするにはreset({ hoge: "" })とするか、setValueを使用してsetValue("hoge", "")とすればできました。

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【React】SWRで定期的に再検証する方法https://dabohaze.site/swr-refreshinterval/Mon, 18 Sep 2023 12:22:05 +0000https://dabohaze.site/?p=3899SWRで定期的に再検証する方法です。

refreshIntervalを使用すると実現できます。

"use client";
import { useEffect, useState } from "react";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export default function Home() { const [status, setStatus] = useState(false); const { data } = useSWR("http://localhost:3100/api/users", fetcher, { refreshInterval: !status ? 5000 : 0, }); useEffect(() => { if (data.status === "OK") { setStatus(true); } }, [data]); return <>〜省略〜</>;
}

上記の例では、5秒おき(5000ミリ秒)に再検証するようにしています。

refreshIntervalは0を指定すると自動再検証がストップするので、再検証したレスポンスをもとに再検証を止めるようにしています。

setIntervalなどを使って自作する必要がないので便利です。

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【React】React Hook Formのregisterをコンポーネントのpropsに渡すhttps://dabohaze.site/react-hook-form-register-props/Sun, 27 Aug 2023 07:28:53 +0000https://dabohaze.site/?p=3888Controllerを使用せずにコンポーネントに対してReact Hook Formのregisterを渡したいことがあったのでメモ。

"use client";
import Text from "@/components/Text";
import { useForm } from "react-hook-form";
type SampleForm = { sampleText: string;
};
export default function Home() { const { register, handleSubmit, watch } = useForm<SampleForm>(); const sampleText = watch("sampleText"); console.log(sampleText); const onSubmit = (data: SampleForm) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Text label="サンプルインプット" register={register("sampleText")} /> <button type="submit">Submit</button> </form> );
}
import { InputHTMLAttributes } from "react";
import { UseFormRegisterReturn } from "react-hook-form";
type InputProps = InputHTMLAttributes<HTMLInputElement> & { label: string; register: UseFormRegisterReturn;
};
const Text = ({ label, register, ...otherProps }: InputProps) => { return ( <div> <label>{label}</label> <input {...register} {...otherProps} /> </div> );
};
export default Text;

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【React】React×TailwindCSSでアニメーションを実装するhttps://dabohaze.site/react-tailwindcss-animation/Sat, 26 Aug 2023 07:39:16 +0000https://dabohaze.site/?p=3882ReactとTailwindCSSでアニメーションを実装しようとして少し詰まったのでメモ。

「ボタンが押されたら1秒かけて文字の不透明度を0から1にする」というアニメーションは下記のようにすれば実現できます。

import { useState } from "react";
export default function Home() { const [animationStart, setAnimationStart] = useState(false); return ( <> <button onClick={() => { setAnimationStart(true); }} > サンプルボタン </button> <div className={`transition-all duration-1000 ease-linear ${ animationStart ? "opacity-100" : "opacity-0" }`} > サンプル </div> </> );
}

「サンプル」のdivタグにtransition-all duration-1000 ease-linearでアニメーションが完了する時間や進行割合などを指定しています。

さらに、animationStart ? "opacity-100" : "opacity-0"animationStartは初期表示時はfalseなので、opacity-0が適用されて文字列「サンプル」は初期表示時は表示されません。

「サンプルボタン」が押されたらanimationStartがtrueになり、「サンプル」のdivタグのclassNameがopacity-0からopacity-100になるので、1秒かけて文字列「サンプル」が表示されます。

アニメーションをしたい要素が常にDOMに存在すれば上記のような書き方でOKですが、問題なのは下記のような場合です。

import { useState } from "react";
export default function Home() { const [animationStart, setAnimationStart] = useState(false); return ( <> <button onClick={() => { setAnimationStart(true); }} > サンプルボタン </button> {animationStart && ( <div className={`transition-all duration-1000 ease-linear ${ animationStart ? "opacity-100" : "opacity-0" }`} > サンプル </div> )} </> );
}

上記のコードでは、animationStartがtrueになるまでは「サンプル」のdivタグはDOMに存在せず、trueになると初めてdivが生成されます。

この場合、アニメーションが適用されずに即座に最終状態が表示されるようです。
※ChatGPTに聞きました

じゃあどうするかと調べたら、CSSのanimationkeyframesを使用したら実現できました。

/** @type {import('tailwindcss').Config} */
module.exports = { mode: "jit", darkMode: false, content: [ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}", "./src/app/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { extend: { keyframes: { sample: { from: { opacity: 0 }, to: { opacity: 1 }, }, }, animation: { sample: "sample 1s linear", }, }, }, plugins: [],
};
import { useState } from "react";
export default function Home() { const [animationStart, setAnimationStart] = useState(false); return ( <> <button onClick={() => { setAnimationStart(true); }} > サンプルボタン </button> {animationStart && <div className={"animate-sample"}>サンプル</div>} </> );
}

tailwind.config.jsにanimationkeyframesを定義しています。
animationにはアニメーションの名前、アニメーションの秒数、進行割合を指定しています。
keyframesにはアニメーションの始まりと終わりを定義しています。

ReactのコンポーネントではdivタグのclassNameにanimate-sampleを指定することにより、tailwind.config.jsで定義したアニメーションのクラスが適用されます。

これにより、animationStartがfalseの間はdivタグはDOMに存在せず、trueになったらdivタグが生成され、かつ1秒かけて「サンプル」の文字が表示されるアニメーションが実現できました!

最初は「React TailwindCSS アニメーション」みたいな感じで調べていて、間違ったアプローチをしていたので少し詰まりました。

「display none アニメーション CSS」みたいな感じで調べたら今回の解決法に行きつきました。

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
分割代入と残余引数でオブジェクトの特定のプロパティ以外を全て取り出すhttps://dabohaze.site/js-destructuring-assignment-rest-parameter/Sun, 20 Aug 2023 11:54:11 +0000https://dabohaze.site/?p=3878分割代入と残余引数を使えば、オブジェクトから「特定のプロパティ以外」を全て取り出すことができます。

const profile = { id: 1, name: "hoge", age: 30, gender: "man", height: 165, weight: 55, bloodType: "O"
}
const {id, ...newProfile} = profile;
console.log(newProfile);
// {"name": "hoge", "age": 30, "gender": "man", "height": 165, "weight": 55, "bloodType": "O"} 

上の例では、オブジェクトprofileからidを分割代入で取り出し、それ以外のプロパティは残余引数を使ってnewProfileで取り出しています。

Reactでは、「propsの特定のプロパティ以外をそのまま別のコンポーネントに渡す」ときなんかに使えます。

以下の例では、HogeButtonコンポーネントにpropsでbuttonNameonClickdisabledを渡しています。

HogeButtonコンポーネントではbuttonNameを分割代入で取り出し、それ以外は残余引数でhogeButtonPropsとして取り出しています。

そして、Buttonコンポーネントに対してbuttonNameを渡し、それ以外はhogeButtonPropsをスプレッド構文で展開して渡しています。

"use client"
import HogeButton from "@/components/HogeButton";
export default function Home() { return ( <HogeButton buttonName="hoge" onClick={() => { alert("hoge"); }} disabled={false} /> );
}
import { ButtonHTMLAttributes } from "react";
import Button from "./Button";
type HogeButton = { buttonName: string;
} & ButtonHTMLAttributes<HTMLButtonElement>;
const HogeButton = (props: HogeButton) => { const { buttonName, ...hogeButtonProps } = props; return <Button buttonName={buttonName} {...hogeButtonProps} />;
};
export default HogeButton;
import { ButtonHTMLAttributes } from "react";
type Button = { buttonName: string } & ButtonHTMLAttributes<HTMLButtonElement>;
const Button = (props: Button) => { const { buttonName, ...buttonProps } = props; return <button {...buttonProps}>{buttonName}</button>;
};
export default Button;

↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!
にほんブログ村 IT技術ブログへ
にほんブログ村

]]>
【JS】オブジェクトのundefinedのプロパティを削除するhttps://dabohaze.site/object-undefined-propery-delete/Sat, 19 Aug 2023 08:03:33 +0000https://dabohaze.site/?p=3859やりたいこと
const sampleObject = { key1: 1, key2: undefined, key3: 3, key4: 4, key5: undefined,
};

↑↑のようなオブジェクトがあった場合に、undefinedのプロパティを削除して↓↓のようなオブジェクトにしたい

const sampleObject = { key1: 1, key3: 3, key4: 4,
};

解決策

こんな感じでできました!

const newSampleObject = Object.fromEntries( Object.entries(sampleObject).filter(([, value]) => { return value !== undefined; })
);
console.log(newSampleObject);
// {"key1": 1, "key3": 3, "key4": 4} 

解説

Object.entriesでオブジェクトを配列に変換しています。

const sampleObject = { key1: 1, key2: undefined, key3: 3, key4: 4, key5: undefined,
};
console.log(Object.entries(sampleObject));
// [["key1", 1], ["key2", undefined], ["key3", 3], ["key4", 4], ["key5", undefined]]

オブジェクトを配列に変換後、filterで値がundefinedのものを除きます。

const sampleObject = { key1: 1, key2: undefined, key3: 3, key4: 4, key5: undefined,
};
console.log(Object.entries(sampleObject).filter(([, value]) => { return value !== undefined;
}));
// [["key1", 1], ["key3", 3], ["key4", 4]] 

最後に、Object.fromEntriesで配列をオブジェクトに変換して終了です!

const sampleObject = { key1: 1, key2: undefined, key3: 3, key4: 4, key5: undefined,
};
console.log(Object.fromEntries( Object.entries(sampleObject).filter(([, value]) => { return value !== undefined; })
));
// {"key1": 1, "key3": 3, "key4": 4} 

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【Next.js】swrをReact Testing Libraryでmock化するhttps://dabohaze.site/swr-react-testing-library-mock/Fri, 11 Aug 2023 07:49:59 +0000https://dabohaze.site/?p=3847useSWRを使用しているコンポーネントのテストコードをReact Testing Libraryで書く際に、useSWRをmock化する必要がありました。

下記のようにしてuseSWRをmock化できました。

"use client";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
const Sample = () => { const { data, error } = useSWR("http://localhost:3100/api/users", fetcher); return <>{data.name}</>;
};
export default Sample;
import Sample from "@/components/Sample";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import useSWR from "swr";
jest.mock("swr");
describe("Sample", () => { (useSWR as jest.Mock).mockReturnValue({ data: { name: "test", }, }); test("test", () => { render(<Sample />); expect(screen.getByText("test")).toBeInTheDocument(); });
});

↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村

]]>
【Next.js】React Testing LibraryでFormの入力値のテストをするhttps://dabohaze.site/react-testing-library-form-value-test/Sat, 29 Jul 2023 02:47:59 +0000https://dabohaze.site/?p=3833React Testing LibraryでFormの入力値が正しいかテストする方法です。

toBetoHaveValueで値が取得できると思っていましたができなかったので備忘録です。

"use client";
import { useForm, SubmitHandler } from "react-hook-form";
type SampleForm = { sampleName: string;
};
const Sample = () => { const { register, handleSubmit, formState: { errors }, } = useForm<SampleForm>({ defaultValues: { sampleName: "sampleValue", }, }); const onSubmit = (data: SampleForm) => { console.log(data); }; return ( <form onSubmit={handleSubmit(onSubmit)} data-testid="test-form"> <input type="text" {...register("sampleName")} />; </form> );
};
export default Sample;

import Sample from "@/components/Sample";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
describe("Sample", () => { it("textbox", () => { render(<Sample />); expect(screen.getByTestId("test-form")).toHaveFormValues({ sampleName: "sampleValue", }); });
});

toHaveFormValuesを使用してテキストボックスsampleNameの初期値がsampleValueであることのテストができました!

↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

]]>
React Testing LibraryでqueryByTextとgetByTextの違いhttps://dabohaze.site/react-testing-library-querybytext-getbytext/Sun, 23 Jul 2023 09:20:38 +0000https://dabohaze.site/?p=3830React Testing LibraryでqueryByTextgetByTextの違いがわかっていなかったのでメモ。

queryByTextの場合は対象の文字列がなかった場合はテストが失敗するだけですが、getByTextの場合は対象の文字列がなかった場合はエラーになりました。

const Sample = () => { return <div>Sample</div>;
};
export default Sample;
import "@testing-library/jest-dom";
import Sample from "@/components/Sample";
import { render, screen } from "@testing-library/react";
describe("Sample", () => { it("queryByText", () => { render(<Sample />); expect(screen.queryByText("test")).not.toBeInTheDocument(); }); it("getByText", () => { render(<Sample />); expect(screen.getByText("test")).not.toBeInTheDocument(); });
});

上記の2つのテストケースはどちらもpassしそうですが、1つ目のケースはテストが通りましたが2つ目は下記エラーが発生しました。

TestingLibraryElementError: Unable to find an element with the text: test.
This could be because the text is broken up by multiple elements.
In this case, you can provide a function for your text matcher to make your matcher more flexible.

「〇〇が表示されないこと」みたいなテストケースを書くときはqueryByTextを使用する方がよさそうです。

]]>