Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
というエラーに出会ったのでメモ。
環境
- React
^17.0.1
エラーが発生した経緯
ロード中と完了後に別のコンポーネントを表示したいとか、状態が違う時に別のコンポーネントを表示したいとか JSX 内に分岐がある箇所に Hooks を使うとエラーになるっぽい。
エラーの再現ができなかったのですが、map でループして表示させているコンポーネントを条件分岐させて、片方で props を整形させる hooks を使っていると上記のエラーになりました。
const Articles: VFC<ArticlesProps> = (props) => { const [isEdit, setIsEdit] = useState(false); const onChangeMode = useCallback((mode: boolean) => () => { setIsEdit(mode); }, []); return isEdit ? <ArticleEditForm onEditEnd={onChangeMode(false)} {...props} /> : <ArticleBody {...useBuildArticleProps(props)} onEdit={onChangeMode(true)}>; }; cost App: VFC = () => { const { isLoading, data } = useLoadArticles(); return ( <div> {isLoading ? <Loading /> : <ul>{data.map((item) => <Articles {...item} />)}</ul>} </div> ); };
=> Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
条件によって Hooks の数が変わってしまう事が原因なようで、useBuildArticleProps
を条件式で上の JSX ではなく、呼び出したコンポーネント内に移動させればエラーにはならなくなりました。
// 別のコンポーネントにしてその中から Hooks を呼び出せばOK const ArticleContainer: VFV<ArticleContainerProps> = ({onEdit, ...props}) => { return <ArticleBody {...useBuildArticleProps(props)} onEdit={onChangeMode(true)}>; }; const Articles: VFC<ArticlesProps> = (props) => { const [isEdit, setIsEdit] = useState(false); const onChangeMode = useCallback((mode: boolean) => () => { setIsEdit(mode); }, []); return isEdit ? <ArticleEditForm onEditEnd={onChangeMode(false)} {...props} /> : <ArticleContainer onEdit={onChangeMode(true)} {…props}>; };
ESLint で eslint-plugin-react-hooks を使っていると、条件付きで Hooks を呼び出す箇所をエラーにしてくれる
cost App: VFC = () => { const { isLoading, data } = useLoadArticles(); return ( <div> {isLoading ? <Loading /> : <Articles {…useBuildProps(data)} />} </div> ); };
=> React Hook "useBuildProps" is called conditionally. React Hooks must be called in the exact same order in every component render
※ ESLint で eslint-plugin-react-hooks
を導入してない場合このエラーは表示されない
useBuildProps
を条件分岐で呼び出した別のコンポーネント内で実行するようにすればOK
const ArticleContainer: VFC<ArticleContainerProps> = ({ data }) => { return <Article {…useBuildProps(data)} />; }; cost App: VFC = () => { const { isLoading, data } = useLoadArticles(); return ( <div> {isLoading ? <Loading /> : <ArticleContainer data=(data) />} </div> ); };
条件分岐したコンポーネントで Hooks が呼ばれてるから同じなのでは?と感じてしまうのですが React 的には別のコンポーネント内に閉じていると問題ないっぽい。
所管
ESLint の eslint-plugin-react-hooks
プラグインを使っていれば、条件分岐の JSX 内で Hooks を呼び出しているとエラーを出してくれるので事前に回避することができそうです。
[参考]
- javascript - React JS: Rendered fewer hooks than expected. This may be caused by an accidental early return statement - Stack Overflow
- 解決【React】Uncaught Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement.」 | 武骨日記
- How to fix React Error: Rendered fewer hooks than expected | by Jon Church | Medium
実践TypeScript ~ BFFとNext.js&Nuxt.jsの型定義~
- 作者:吉井 健文
- 発売日: 2019/06/26
- メディア: 単行本(ソフトカバー)
Clover スプリングホック No.1 12組入り シルバー 26-515
- メディア: おもちゃ&ホビー