nextArrayBad는 array와 같은 주소값을 참조하는 것
nextArrayBad의 값을 변경하면 원본 배열인 array의 값도 함께 변경됨
nextArrayGood은 array를 복제하여 만든 것
nextArrayGood의 값을 변경해도 원본 배열인 array의 값은 변경되지 않는다.
AppWithoutImmer.js
import { useRef, useState, useCallback } from "react";
function AppWithoutImmer() {
const nextId = useRef(1);
const [form, setForm] = useState({ name: "", username: "" });
const [data, setData] = useState({
array: [],
uselessValue: null,
});
const handleChange = useCallback(
(e) => {
const { name, value } = e.target;
setForm({
...form,
[name]: value,
});
},
[form]
);
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username,
};
setData({
...data,
array: data.array.concat(info),
});
setForm({
name: "",
username: "",
});
nextId.current += 1;
},
[data, form.name, form.username]
);
const handleRemove = useCallback(
(id) => {
setData({
...data,
array: data.array.filter((info) => info.id !== id),
});
},
[data]
);
return (
<form onSubmit={handleSubmit}>
<input
name="username"
type="text"
placeholder="아이디"
value={form.username}
onChange={handleChange}
/>
<input
name="name"
type="text"
placeholder="이름"
value={form.name}
onChange={handleChange}
/>
<button type="submit">등록</button>
<ul>
{data.array.map((info) => (
<li key={info.id} onClick={() => handleRemove(info.id)}>
{info.username} {info.name}
</li>
))}
</ul>
</form>
);
}
export default AppWithoutImmer;
with Immer
import { useRef, useState, useCallback } from "react";
import produce from "immer";
function App() {
const nextId = useRef(1);
const [form, setForm] = useState({ name: "", username: "" });
const [data, setData] = useState({
array: [],
uselessValue: null,
});
const handleChange = useCallback((e) => {
const { name, value } = e.target;
setForm(
produce((draft) => {
draft[name] = value;
})
);
}, []);
const handleSubmit = useCallback(
(e) => {
e.preventDefault();
const info = {
id: nextId.current,
name: form.name,
username: form.username,
};
setData(
produce((draft) => {
draft.array.push(info);
})
);
setForm({
name: "",
username: "",
});
nextId.current += 1;
},
[form.name, form.username]
);
const handleRemove = useCallback(
(id) => {
setData(
// 소스코드가 복잡해질 때는 그냥 immer 사용하지 않고 작성
{
...data,
array: data.array.filter((info) => info.id !== id),
}
// produce(data, (draft) => {
// draft.array.splice(
// draft.array.findIndex((info) => info.id !== id),
// 1
// );
// // pop을 쓰면 제일 마지막의 요소부터 순서대로 pop되기 때문에 적합하지 않음
// // draft.array.pop((info) => info.id === id);
// })
);
},
[data]
);
return (
<form onSubmit={handleSubmit}>
<input
name="username"
type="text"
placeholder="아이디"
value={form.username}
onChange={handleChange}
/>
<input
name="name"
type="text"
placeholder="이름"
value={form.name}
onChange={handleChange}
/>
<button type="submit">등록</button>
<ul>
{data.array.map((info) => (
<li key={info.id} onClick={() => handleRemove(info.id)}>
{info.username} {info.name}
</li>
))}
</ul>
</form>
);
}
export default App;
Index.js
...
import {BrowserRouter} from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
...
App.js
import "./App.css";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
function App() {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
);
}
export default App;
리액트 라우터 사용시에는 Link 태그 이용
(a 태그 이용하면 브라우저에서 페이지를 새로 불러오기 때문)
Home.js
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
return (
<div>
<h1>Home</h1>
<p>The First Page</p>
<Link to="/about">Move to About Page</Link>
</div>
);
};
export default Home;
About.js
import React from "react";
import { useLocation } from "react-router-dom";
const About = () => {
const location = useLocation();
return (
<div>
<h1>Intro</h1>
<p>Project Using Router</p>
{/* search : query string included "?" */}
<p>{location.search}</p>
</div>
);
};
export default About;
Home.js
import React from "react";
import { Link } from "react-router-dom";
const Home = () => {
return (
<div>
<h1>Home</h1>
<p>The First Page</p>
<ul>
<li>
<Link to="/about">Move to About Page</Link>
</li>
<li>
<Link to="/profile/yeji">yeji's profile</Link>
</li>
<li>
<Link to="/profile/hong">hong's profile</Link>
</li>
<li>
<Link to="/profile/void">Not Existing profile</Link>
</li>
</ul>
</div>
);
};
export default Home;
App.js
import "./App.css";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Profile from "./components/Profile";
function App() {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/profile/:name" element={<Profile />}></Route>
</Routes>
);
}
export default App;
About.js
import React from "react";
import { useSearchParams } from "react-router-dom";
const About = () => {
const [searchParam, setSearchParam] = useSearchParams();
const detail = searchParam.get("detail");
const mode = searchParam.get("mode");
const onToggleDetail = () => {
setSearchParam({
mode,
detail: detail === "true" ? false : true,
});
};
const inIncreaseMode = () => {
const nextMode = mode === "null" ? 1 : parseInt(mode) + 1;
setSearchParam({
mode: nextMode,
detail,
});
};
return (
<div>
<h1>Intro</h1>
<p>Project Using Router</p>
{/* search : query string included "?" */}
{/* <p>{location.search}</p> */}
<p>detail: {detail}</p>
<p>mode: {mode}</p>
<button type="button" onClick={onToggleDetail}>
Toggle detail
</button>
<button type="button" onClick={inIncreaseMode}>
mode + 1
</button>
</div>
);
};
export default About;
Articles.js
import React from "react";
import { Link, Outlet } from "react-router-dom";
const Articles = () => {
return (
<ul>
{/* Route의 children으로 들어가는 JSX element를 보여줌 */}
<Outlet />
<li>
<Link to="/articles/1">post 1</Link>
</li>
<li>
<Link to="/articles/2">post 2</Link>
</li>
<li>
<Link to="/articles/3">post 3</Link>
</li>
</ul>
);
};
export default Articles;
Article.js
import React from "react";
import { useParams } from "react-router-dom";
const Article = () => {
const { id } = useParams();
return (
<div>
<h2>post {id}</h2>
</div>
);
};
export default Article;
App.js
import "./App.css";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Profile from "./components/Profile";
import Articles from "./components/Articles";
import Article from "./components/Article";
function App() {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/profile/:name" element={<Profile />}></Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
</Routes>
);
}
export default App;
<Outlet />
을 이용하면 리스트가 밑에 그대로 보이고 상단에 라우팅한 페이지 표출됨
Layout.js
import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
const Layout = () => {
const navigate = useNavigate();
const goBack = () => {
navigate(-1);
}
const goArticles = () => {
navigate('/articles');
}
return (
<div>
<header style={{ background: "lightgray", padding: 16, fontSize: 24 }}>
Header
<button type="button" onClick={goBack}>Back</button>
<button type="button" onClick={goArticles}>go to post list</button>
</header>
<main>
<Outlet />
</main>
</div>
);
};
export default Layout;
App.js
import "./App.css";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Profile from "./components/Profile";
import Articles from "./components/Articles";
import Article from "./components/Article";
import Layout from "./components/Layout";
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/profile/:name" element={<Profile />}></Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
</Route>
</Routes>
);
}
export default App;
Articles.js
import React from "react";
import { NavLink, Outlet } from "react-router-dom";
const Articles = () => {
const activeStyle = {
color: "green",
fontSize: 21,
};
return (
<ul>
{/* Route의 children으로 들어가는 JSX element를 보여줌 */}
<Outlet />
<li>
<NavLink
to="/articles/1"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
post 1
</NavLink>
</li>
<li>
<NavLink
to="/articles/2"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
post 2
</NavLink>
</li>
<li>
<NavLink
to="/articles/3"
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
post 3
</NavLink>
</li>
</ul>
);
};
export default Articles;
Articles.js
import React from "react";
import { NavLink, Outlet } from "react-router-dom";
const ArticleItem = ({ id }) => {
const activeStyle = {
color: "green",
fontSize: 21,
};
return (
<li>
<NavLink
to={`/articles/${id}`}
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
post {id}
</NavLink>
</li>
);
};
const Articles = () => {
return (
<ul>
{/* Route의 children으로 들어가는 JSX element를 보여줌 */}
<Outlet />
<ArticleItem id={1} />
<ArticleItem id={2} />
<ArticleItem id={3} />
</ul>
);
};
export default Articles;
Login.js
import React from 'react';
const Login = () => {
return (
<div>
Login Page
</div>
);
};
export default Login;
Mypage.js
import React from 'react';
import { Navigate } from 'react-router-dom';
const Mypage = () => {
const isLogged = false;
if(!isLogged) {
return <Navigate to='/login' />
}
return (
<div>
My Page
</div>
);
};
export default Mypage;
App.js
import "./App.css";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Profile from "./components/Profile";
import Articles from "./components/Articles";
import Article from "./components/Article";
import Layout from "./components/Layout";
import NotFound from "./components/NotFound";
import Login from "./components/Login";
import Mypage from "./components/Mypage";
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/profile/:name" element={<Profile />}></Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
<Route path="/login" element={<Login />}></Route>
<Route path="/mypage" element={<Mypage />}></Route>
</Route>
<Route path="*" element={<NotFound />}></Route>
</Routes>
);
}
export default App;