Invalid hook call - hooks을 모듈화 할 수 있는가?
hooks 모듈화
Header.jsx
import { useState } from "react";
import { movePath } from "../Utils";
import MenuIcon from "@mui/icons-material/Menu";
import SearchIcon from "@mui/icons-material/Search";
import NotificationsIcon from "@mui/icons-material/Notifications";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
const SearchBar = () => {
const [search, setSearch] = useState("");
const handleSubmitSearch = (e) => {
e.preventDefault();
console.log(search); // 검색 창으로 이동해야 함.
movePath(`/search?keyword=${search}`);
setSearch("");
};
const handleChangeSearch = (e) => {
setSearch(e.currentTarget.value);
};
<div>
<form onSubmit={handleSubmitSearch}>
<input
onChange={handleChangeSearch}
type="text"
maxLength="12"
className="search"
value={search}
placeholder="게시물 제목 검색"
/>
<button onClick={handleSubmitSearch}>검색</button>
</form>
</div>;
};
const Header = ({ handleOpenMenu, isSearch }) => {
return (
<div className="mainpage-header">
<MenuIcon sx={{ color: "white" }} onClick={handleOpenMenu} />
<SearchIcon sx={{ color: "white" }} onClick={() => movePath("/search")} />
<img src="assets/headerLogo.svg" alt="header-logo" />
<NotificationsIcon sx={{ color: "white" }} />
<AccountCircleIcon
sx={{ color: "white" }}
onClick={() => movePath("/profile")}
/>
{isSearch && <SearchBar />}
</div>
);
};
export default Header;
movePath.jsx
import { useNavigate } from "react-router-dom";
const movePath = (path) => {
const navi = useNavigate();
console.log(path);
navi(path);
};
export default movePath;
import { useState } from "react";
import { movePath } from "../Utils";
import MenuIcon from "@mui/icons-material/Menu";
import SearchIcon from "@mui/icons-material/Search";
import NotificationsIcon from "@mui/icons-material/Notifications";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
const SearchBar = () => {
const [search, setSearch] = useState("");
const handleSubmitSearch = (e) => {
e.preventDefault();
console.log(search); // 검색 창으로 이동해야 함.
movePath(`/search?keyword=${search}`);
setSearch("");
};
const handleChangeSearch = (e) => {
setSearch(e.currentTarget.value);
};
<div>
<form onSubmit={handleSubmitSearch}>
<input
onChange={handleChangeSearch}
type="text"
maxLength="12"
className="search"
value={search}
placeholder="게시물 제목 검색"
/>
<button onClick={handleSubmitSearch}>검색</button>
</form>
</div>;
};
const Header = ({ handleOpenMenu, isSearch }) => {
return (
<div className="mainpage-header">
<MenuIcon sx={{ color: "white" }} onClick={handleOpenMenu} />
<SearchIcon sx={{ color: "white" }} onClick={() => movePath("/search")} />
<img src="assets/headerLogo.svg" alt="header-logo" />
<NotificationsIcon sx={{ color: "white" }} />
<AccountCircleIcon
sx={{ color: "white" }}
onClick={() => movePath("/profile")}
/>
{isSearch && <SearchBar />}
</div>
);
};
export default Header;
import { useNavigate } from "react-router-dom";
const movePath = (path) => {
const navi = useNavigate();
console.log(path);
navi(path);
};
export default movePath;
다음처럼 useNavigate를 사용하는 부분이 중복되어서 모듈에서 useNavigate를 할당받아서 이동을 대신 해주려고 했다. 그런데...
Invalid hook call
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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
테스트를 해보니 에러를 뿜는다. 파파고의 힘을 빌려보니 이렇게 말하더라.
- 잘못된 후크 호출입니다. 후크는 함수 구성요소의 본체 내부에서만 호출할 수 있습니다. 이 문제는 다음 이유 중 하나로 인해 발생할 수 있습니다.
- React와 렌더러의 버전이 일치하지 않을 수 있습니다(예: React DOM).
- hooks 룰을 어기는 것일 수도 있습니다.
- 동일한 앱에 React 사본이 두 개 이상 있을 수 있습니다.
hooks 룰 - Only Call Hooks from React Functions
https://reactjs.org/docs/hooks-rules.html
금방 해답이 나왔다. 훅은 항상 리액트 컴포넌트 내부에서만 호출해야 되었던 것이다. 바로 밑에서도 일반 자바스크립트 코드에서 호출하지 말라고 명시하고 있다. 컴포넌트에서 호출하거나 커스텀 훅으로 호출하는 방법 이외로 호출할 수 없는 것이다. 그렇다면 방법은 간단해진다.
해결
Header.jsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { movePath } from "../Utils";
import MenuIcon from "@mui/icons-material/Menu";
import SearchIcon from "@mui/icons-material/Search";
import NotificationsIcon from "@mui/icons-material/Notifications";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
const SearchBar = () => {
const [search, setSearch] = useState("");
const navi = useNavigate();
const handleSubmitSearch = (e) => {
e.preventDefault();
console.log(search); // 검색 창으로 이동해야 함.
movePath(navi, "search", `?keyword=${search}`);
setSearch("");
};
const handleChangeSearch = (e) => {
setSearch(e.currentTarget.value);
};
<div>
<form onSubmit={handleSubmitSearch}>
<input
onChange={handleChangeSearch}
type="text"
maxLength="12"
className="search"
value={search}
placeholder="게시물 제목 검색"
/>
<button onClick={handleSubmitSearch}>검색</button>
</form>
</div>;
};
const Header = ({ handleOpenMenu, isSearch }) => {
const navi = useNavigate();
return (
<div className="mainpage-header">
<MenuIcon sx={{ color: "white" }} onClick={handleOpenMenu} />
<SearchIcon
sx={{ color: "white" }}
onClick={() => movePath(navi, "/search")}
/>
<img
src="assets/headerLogo.svg"
alt="header-logo"
onClick={() => movePath(navi, "/")}
/>
<NotificationsIcon sx={{ color: "white" }} />
<AccountCircleIcon
sx={{ color: "white" }}
onClick={() => movePath(navi, "/profile")}
/>
{isSearch && <SearchBar />}
</div>
);
};
export default Header;
movePath.jsx
const movePath = (navi, path, query) => {
if (query === undefined) navi(path);
else navi(path + query);
};
export default movePath;
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { movePath } from "../Utils";
import MenuIcon from "@mui/icons-material/Menu";
import SearchIcon from "@mui/icons-material/Search";
import NotificationsIcon from "@mui/icons-material/Notifications";
import AccountCircleIcon from "@mui/icons-material/AccountCircle";
const SearchBar = () => {
const [search, setSearch] = useState("");
const navi = useNavigate();
const handleSubmitSearch = (e) => {
e.preventDefault();
console.log(search); // 검색 창으로 이동해야 함.
movePath(navi, "search", `?keyword=${search}`);
setSearch("");
};
const handleChangeSearch = (e) => {
setSearch(e.currentTarget.value);
};
<div>
<form onSubmit={handleSubmitSearch}>
<input
onChange={handleChangeSearch}
type="text"
maxLength="12"
className="search"
value={search}
placeholder="게시물 제목 검색"
/>
<button onClick={handleSubmitSearch}>검색</button>
</form>
</div>;
};
const Header = ({ handleOpenMenu, isSearch }) => {
const navi = useNavigate();
return (
<div className="mainpage-header">
<MenuIcon sx={{ color: "white" }} onClick={handleOpenMenu} />
<SearchIcon
sx={{ color: "white" }}
onClick={() => movePath(navi, "/search")}
/>
<img
src="assets/headerLogo.svg"
alt="header-logo"
onClick={() => movePath(navi, "/")}
/>
<NotificationsIcon sx={{ color: "white" }} />
<AccountCircleIcon
sx={{ color: "white" }}
onClick={() => movePath(navi, "/profile")}
/>
{isSearch && <SearchBar />}
</div>
);
};
export default Header;
const movePath = (navi, path, query) => {
if (query === undefined) navi(path);
else navi(path + query);
};
export default movePath;
hooks로 할당받은 함수를 인자로 넘겨주고 모듈 함수 내부에서 콜백함수로 사용하게 되면 의도했던 대로 작동하게 한다.
Author And Source
이 문제에 관하여(Invalid hook call - hooks을 모듈화 할 수 있는가?), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sham/Invalid-hook-call-hooks을-모듈화-할-수-있는가저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)