エラー文
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app
翻訳
捕捉されないエラー: 無効なフック呼び出しです。フックはファンクション・コンポーネントのボディの内部でのみ呼び出すことができます。これは、以下のいずれかの理由で発生する可能性があります:
- Reactとレンダラー(React DOMなど)のバージョンが不一致である。
- フックのルールを破っている可能性がある。
- 同じアプリに複数のReactコピーがあるかもしれない。
予想
- Reactとレンダラー(React DOMなど)のバージョンが不一致である。
→他の箇所でエラーしていないし、可能性は低い。 - フックのルールを破っている可能性がある。
→可能性あり。 - 同じアプリに複数のReactコピーがあるかもしれない。
→ない。
これらから、hooks の使い方を間違えている?
コード
関係ありそうなコードは下記。
templates/TodoCreateTemplate/index.jsx
/**
* TodoTemplate
*
* @package components
*/
import { useTodoContext } from '../../../contexts/TodoContext';
import { InputForm } from '../../atoms/InputForm';
import { TextAreaForm } from '../../atoms/TextAreaForm';
import { CommonButton } from '../../atoms/CommonButton';
import { useTodoCreateTemplate } from './useTodoCreateTemplate';
import styles from './styles.module.css';
export const TodoCreateTemplate = () => {
const { addTodo } = useTodoContext();
const [
{ inputTitle, inputContent },
{ handleChangeTitle, handleChangeContent, handleCreateTodo },
] = useTodoCreateTemplate({ addTodo });
/**
* TodoTemplate
* @returns {JSX.Element}
* @constructor
*/
return (
<div className={styles.container}>
<div className={styles.titleBox}>
<h2>Create Todo</h2>
</div>
<form onSubmit={handleCreateTodo}>
<InputForm inputVal={inputTitle} placeholder={'Title'} onChange={handleChangeTitle} />
<TextAreaForm
inputVal={inputContent}
placeholder={'Content'}
onChange={handleChangeContent}
/>
<CommonButton label={'Create Todo'} type={'submit'} />
</form>
</div>
);
};
templates/TodoCreateTemplate/useTodoCreateTemplate.js
/**
* useTodoCreateTemplate
*
* @package hooks
*/
import { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { TODO_PATH } from '../../../constants/pagenations';
~~~
/**
* Todo の新規登録処理
* @type {function(*): void |*}
*/
const handleCreateTodo = useCallback(
() => {
if (inputTitle && inputContent) {
addTodo(inputTitle, inputContent);
useNavigate(TODO_PATH.TOP);
}
},
// これらが更新された時のみ、関数を再生成する
[addTodo, inputTitle, inputContent, navigate],
);
~~~
};
/hooks/useTodo.js
import { useState, useMemo, useCallback } from 'react';
import { INIT_TODO_LIST } from '../constants/data';
~~~
export const useTodo = () => {
~~~
/**
* Todo 新規登録処理
* @param {*} e
*/
const addTodo = useCallback((title, content) => {
const newId = originTodoList.length + 1;
const newTodo = {
id: newId,
title: title,
content: content,
};
setOriginTodoList([...originTodoList, newTodo]);
console.log(originTodoList);
});
~~~
};
試したこと
- hooks/useTodo.js の処理をコメントアウトしてもエラー解消しない。
- TodoCreateTemplate/index.jsx はインポートして使用しているだけなので、原因である可能性は低いか。
→ TodoCreateTemplate/useTodoCreateTemplate.jsが原因か?
https://qiita.com/tatsumin0206/items/4e1076e2deedf20a9485
の、ルール① フックはトップレベルでしか呼び出せない が当てはまるような気がする。
解決
原因は、
templates/TodoCreateTemplate/useTodoCreateTemplate.js
/**
* useTodoCreateTemplate
*
* @package hooks
*/
import { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { TODO_PATH } from '../../../constants/pagenations';
~~~
/**
* Todo の新規登録処理
* @type {function(*): void |*}
*/
const handleCreateTodo = useCallback(
() => {
if (inputTitle && inputContent) {
addTodo(inputTitle, inputContent);
useNavigate(TODO_PATH.TOP);
}
},
// これらが更新された時のみ、関数を再生成する
[addTodo, inputTitle, inputContent, navigate],
);
~~~
};
の、 useNavigate(TODO_PATH.TOP); だった。
templates/TodoCreateTemplate/useTodoCreateTemplate.js
const handleCreateTodo = useCallback(
() => {
if (inputTitle && inputContent) {
addTodo(inputTitle, inputContent);
useNavigate(TODO_PATH.TOP);
↑これ
}
},
};
templates/TodoCreateTemplate/useTodoCreateTemplate.js
const navigate = useNavigate();
const handleCreateTodo = useCallback(
() => {
if (inputTitle && inputContent) {
addTodo(inputTitle, inputContent);
navigate(TODO_PATH.TOP);
}
},
};
とすることで解決。お作法的なことだろうか…?