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를 할당받아서 이동을 대신 해주려고 했다. 그런데...
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
테스트를 해보니 에러를 뿜는다. 파파고의 힘을 빌려보니 이렇게 말하더라.
https://reactjs.org/docs/hooks-rules.html
금방 해답이 나왔다. 훅은 항상 리액트 컴포넌트 내부에서만 호출해야 되었던 것이다. 바로 밑에서도 일반 자바스크립트 코드에서 호출하지 말라고 명시하고 있다. 컴포넌트에서 호출하거나 커스텀 훅으로 호출하는 방법 이외로 호출할 수 없는 것이다. 그렇다면 방법은 간단해진다.
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로 할당받은 함수를 인자로 넘겨주고 모듈 함수 내부에서 콜백함수로 사용하게 되면 의도했던 대로 작동하게 한다.