ダボハゼのブログhttps://dabohaze.siteゆるく適当にSun, 06 Oct 2024 12:40:10 +0000jahourly1https://dabohaze.site/wp-content/uploads/2019/01/cropped-icon_site-32x32.pngダボハゼのブログhttps://dabohaze.site3232 Chromeでプラグインなしで画面全体のスクリーンショットを取得する方法https://dabohaze.site/google-chrome-developer-tool-screenshot/Sun, 06 Oct 2024 12:39:05 +0000https://dabohaze.site/?p=3983Google Chromeでデベロッパーツールを使用して、プラグインなしで画面全体のスクリーンショットを取得する方法です。

プラグイン使ったほうが楽ですが、セキュリティの関係でプラグイン使用不可の場合もあると思うので、そんなときに使えます。

まず、Chromeでデベロッパーツールを開きます。

Screenshot

Macの場合はキーボードでCmd + Shift + Pを押します。

すると、以下のようなウィンドウが表示されます。

表示されたウィンドウで「スクリーン」と入力すると、以下のようにコマンドが絞り込まれて表示されるので、「フルサイズのスクリーンショットをキャプチャ」をクリックすれば、全画面のスクリーンショットを取得できます!

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

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

にほんブログ村

]]>
【React】「React has detected a change in the order of Hooks called by〜」の対処法https://dabohaze.site/react-hooks-top-level/Mon, 26 Aug 2024 06:52:42 +0000https://dabohaze.site/?p=3970エラー概要

Reactで「React has detected a change in the order of Hooks called by〜 . This will lead to bugs and errors if not fixed.」のエラーが発生した場合の対処法です。

上記エラーは、useStateなどのHooksが呼び出される順序が変わったときに発生するそうです。

Reactでは、Hooksはトップレベルで呼び出す必要があります。

フックをループや条件分岐、あるいはネストされた関数内で呼び出してはいけません。代わりに、あなたの React の関数のトップレベルでのみ、あらゆる早期 return 文よりも前の場所で呼び出してください。これを守ることで、コンポーネントがレンダーされる際に毎回同じ順番で呼び出されるということが保証されます。これが、複数回 useState や useEffect が呼び出された場合でも React がフックの状態を正しく保持するための仕組みです

参考:https://ja.legacy.reactjs.org/docs/hooks-rules.html

そのため、if文や下記のような早期return文の後にHooksを書くとHooksの順序が変わってしまいエラーが発生します。

import { useEffect, useState } from "react";
const Sample = ({ count }: { count: number }) => { const [flag1, setFlag1] = useState(true); if (count === 0) { return <></>; } const [flag2, setFlag2] = useState(true); return <>{count}</>;
};
export default Sample;

対処法その1:Hooksは関数のトップレベル、早期return文より前に書く

Hooksは条件分岐内や早期return文の後ではなく、トップレベルに書く必要があります。

下記のように、早期return文より前にHooksを書けばエラーは発生しなくなります。

"use state";
import { useEffect, useState } from "react";
const Sample = ({ count }: { count: number }) => { const [flag1, setFlag1] = useState(true); const [flag2, setFlag2] = useState(true); if (count === 0) { return <></>; } return <>{count}</>;
};
export default Sample;

対処法その2:コンポーネントとして切り出す

下記のように、コンポーネントとして切り出した場合もエラーは発生しません。

import { useEffect, useState } from "react";
const Parent = ({ count }: { count: number }) => { const [flag1, setFlag1] = useState(true); if (count === 0) { return <></>; } return <Children count={count} />;
};
const Children = ({ count }: { count: number }) => { const [flag2, setFlag2] = useState(true); return <>{count}</>;
};
export default Parent;

そもそもの話

VSCodeでeslintの設定されていれば、コード書いているときに以下のようにエラーを表示してくれるので気付ける。

React Hook "useState" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?

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

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

にほんブログ村

]]>
Chromeのデベロッパーツールの便利な使い方https://dabohaze.site/chrome-developer-tool/Mon, 22 Jul 2024 09:32:45 +0000https://dabohaze.site/?p=3897フロントエンド開発をしていると、ブラウザのデベロッパーツールをよく使います。

Chromeのデベロッパーツールで知っておくと便利な使い方をご紹介します。

日本語化

デベロッパーツールを開き、右上の歯車 > 言語 で日本語を選択し、デベロッパーツールを開き直すと日本語になります。

画面遷移してもconsole.logを残す

変数や条件分岐でconsole.logを出して検証するということはよくあると思いますが、画面遷移をすると消えてしまいます。

コンソールタブ上部にある「ログを保持」をチェックをすると画面遷移してもログが消えずに残ります。

画面遷移してもネットワークのログを保持

ネットワークタブで、APIのリクエストやレスポンスを確認したいこともよくあると思いますが、これも画面遷移をすると消えてしまいます。

ネットワークタブ上部にある、「ログを保持」をチェックすると画面遷移してもログが消えずに残ります。

ネットワークタブの行を大きく表示

ネットワークタブのログはデフォルトだと以下のように表示されます。

Name列にはAPI名やファイル名が表示されますが、エンドポイントは行を選択してヘッダーを見ないとわかりません。

ネットワークタブ上部にある、「大きなリクエスト行」をタップすると、エンドポイントも表示されるようになります。

低速ネットワークでの確認

低速なネットワークの状態で、どのような挙動になるか確認したいこともあると思います。

ネットワークタブで、「スロットリングなし」をクリックし、「高速 3G」や「低速 3G」を選択すると低速なネットワークでWebアプリの挙動を確認できます。

ローカルストレージ、セッションストレージ、Cookieの確認

ローカルストレージ、セッションストレージ、Cookieは、アプリケーションタブから確認できます。

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

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

にほんブログ村

]]>
iPhoneのSafariで開いているページのデベロッパーツールを開く方法https://dabohaze.site/iphone-safari-developer-tool/Tue, 30 Apr 2024 00:36:50 +0000https://dabohaze.site/?p=3923MacでSafariを使用している場合、Option + Command + i でデベロッパーツールを開き、ネットワークタブやコンソールタブで検証することができます。

ただ、iPhoneのSafariでは単体ではデベロッパーツールを使うことはできません。

iPhoneとMacを使用して、「iPhoneのSafariで開いているページのデベロッパーツールをMacで開く方法」をご紹介します。

やり方

1. MacでSafariを起動します

2. iPhoneとMacをケーブルで接続します

3. iPhoneのSafariを再起動します

4. iPhoneのSafariで、デベロッパーツールを使用したいページを開きます

5. MacのSafariのメニューバーの「開発」をクリックします

6. 以下のような表示になっているはずなので、iPhoneにカーソルを乗せます

7. デベロッパーツールを使用したいページ(タブ)をクリックします

8. iPhoneのSafariで開いているページのデベロッパーツールが起動します!

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

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

にほんブログ村


]]>
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技術ブログへ
にほんブログ村

]]>