1
1

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】クリックイベントでuseEffectを含む処理を発火させたい場合の考え方

Last updated at Posted at 2025-11-03

Reactで開発をしていた際、
useEffectで宣言された処理をボタンの押下(onClick)で呼び出そうとしたのですが詰まってしまったので備忘録として記事にしたいと思います。

やりたいこと

ボタンを押下した段階でuseEffectで宣言しているsetTimeOut関数を呼び出す。

sample1.tsx
const [limit, setLimit] = useState<number>(10000);
const [limitPhrase, setLimitPhrase] = useState<string>("");

// ボタンを押下した時の処理を定数timeStartに指定したい
// const timeStart = () =>{}

// この部分(タイマー処理)をボタン押下で発火させたい
useEffect(() => {
    const timer = setInterval(() => {
      if (limit > 0 && startFlag) {
        setLimit((limit) => limit - 10);
        setLimitPhrase("");
      } else if (limit <= 0) {
        setLimitPhrase("finished");
      } else {
        setLimitPhrase("not start");
      }
    }, 10);
    
    return () => {
      clearInterval(timer);
    };
}, [limit]);

return (
    <>
      <p>{(limit / 1000).toFixed(2)}ms</p>
      <p>{limitPhrase}</p>
      <button onClick={timeStart}>
        START
      </button>
    </>
  );

よくない例

クリックイベントで発火させようとしてonClickの処理内(ここではtimeStart)にuseEffectの記述を入れてしまうとエラーになる(エラー文は後述。)

BadExample.tsx
const timeStart = ()=>{
    useEffect(() => {
        const timer = setInterval(() => {
          if (limit > 0 && startFlag) {
            setLimit((limit) => limit - 10);
            setLimitPhrase("");
          } else if (limit <= 0) {
            setLimitPhrase("finished");
          } else {
            setLimitPhrase("not start");
          }
        }, 10);
        
        return () => {
          clearInterval(timer);
        };
    }, [limit]);
};

発生するエラー文は次の通り。

React Hook "useEffect" is called in function "timeStart" that is neither a React function component nor a custom React Hook function.
React component names must start with an uppercase letter.
React Hook names must start with the word "use".

翻訳ツールで日本語訳すると

Reactフック「useEffect」は、React関数コンポーネントでもカスタムReactフック関数でもない関数「timeStart」内で呼び出されています。
Reactコンポーネント名は大文字で始める必要があります。Reactフック名は「use」で始める必要があります。

となる。

useEffectのような一部のReact Hooksはトップレベルで呼び出さないとエラーになるため。

(公式ドキュメント)

対策例

  • onClickイベントではuseEffect内で使うフラグを設定するようにする。
  • useEffect内部で渡されたフラグを使って条件分岐させる。
GoodExample.tsx
const [limit, setLimit] = useState<number>(10000);
const [limitPhrase, setLimitPhrase] = useState<string>("");
// ボタンのクリックイベントでは下記のフラグの真偽をスイッチさせる
const [startFlag, setStartFlag] = useState<boolean>(false);

const timeStart = () => {
    setStartFlag(true);
};

useEffect(() => {
  const timer = setInterval(() => {
// ボタン押下で指定したフラグがtrueの時に処理が走るように変更
    if (limit > 0 && startFlag) {
      setLimit((limit) => limit - 10);
      setLimitPhrase("");
    } else if (limit <= 0 && startFlag) {
      setLimitPhrase("finished");
    } else {
      setLimitPhrase("not start");
    }
  }, 10);

  return () => {
    clearInterval(timer);
  };
}, [limit, startFlag]);

return (
    <>
      <p>{(limit / 1000).toFixed(2)}ms</p>
      <p>{limitPhrase}</p>
      <button onClick={timeStart}>
        START
      </button>
    </>
  );

上記のようにロジックの視点を変えることで、ボタンの押下に伴ってuseEffectを含む処理を発火させることが可能になる。

最後に

useEffectを単体で使う(例:APIの呼び出し)際と異なり、ボタンの押下などのイベントが絡むケースでは実装に一工夫が必要でした。

同じように詰まっている方々の参考になれば幸いです。

参考資料

(タイマー部分のロジックの参考)

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?