# 리액트 라우터란?
- 요청(특히 url)에 적합한 작업을 수행하는 것
- 리액트 라우터 모듈 설치
1. vs code에서 새로운 cmd 터미널을 열어 프로젝트로 이동
2. npn install react-router-dom
(npm install + 설치할 패키지)
3. App.js 제일 위쪽에 import
import {
BrowserRouteer as Router, Routes, Route, Link, Outlet, useParams
} from "react-router-dom"
# 라우터 사용 / useParams
- Router 컴포넌트로 전체를 감싸주고 Route를 Routes로 묶어준다
- Link태그를 이용하여 텍스트에 주소를 연결한다
- Route 컴포넌트로 각각의 컴포넌트와 주소를 연결한다
<Route path='주소' element={<컴포넌트 />} />
path='*'는 지정한 주소들 외의 모든 주소들을 의미한다. (연결된 컴포넌트가 없는 주소들)
- Post컴포넌트의 주소는 'post/:postId' 이다. 콜론 뒤에 있는 주소는 useParams을 통해 접근할 수 있다.
function App() {
return (
<>
<Router>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/posts">Posts</Link>
</li>
</ul>
</nav>
<hr />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/posts" element={<Posts />} />
<Route path="post/:postId" element={<Post />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
</>
)
}
- useParams: react가 제공하는 Hook. url로 전달된 파라미터에 접근하게 한다
Post 컴포넌트에서 각각 post1과 post2에 접근하기 위해서 useParams으로 postId를 가져온다.
function Home() {
return <h1>Home</h1>
}
function About() {
return <h1>About</h1>
}
function Posts() {
return (
<>
<h1>Posts</h1>
<ul>
<li>
<Link to='/post/p1'>Post1</Link>
</li>
<li>
<Link to='/post/p2'>Post2</Link>
</li>
</ul>
</>
)
}
function Post() {
const params = useParams();
const postId = params.postId;
return (
<>
<h1>Title</h1>
<p>{postId}</p>
</>
)
}
function NotFound() {
return <h1>404 NotFound</h1>
}
# 인증이 적용된 라우터
- 경로에 상관없이 Layout은 그대로 유지되며 경로가 바뀌면 Outlet 부분만 바뀐 경로의 페이지를 출력한다
- index는 부모가 요청한 주소와 동일한 주소를 의미하며 첫화면에 사용한다
- 로그인 상태를 모든 페이지에서 표시할 것이므로 AuthProvider 컴포넌트로 감싼다
- Post 페이지에 접속할 때 인증이 필요하도록 만들기 위해 Post페이지를 input을 출력할 AuthRequired 컴포넌트로 감싼다
function App() {
return (
<Router>
<AuthProvider>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="posts" element={<Posts />} />
<Route path="/post/:postId" element={
<AuthRequired>
<Post />
</AuthRequired>
} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</AuthProvider>
</Router>
)
}
* context
- 일반적인 데이터 전달은 부모 자식간에 props를 통해 명시적으로 전달해야 한다. 따라서 정말 데이터가 필요한 자식에
게 도달하기까지 모든 컴포넌트들이 필요없는 데이터를 공유하게 된다.
- 그러나 현재 로그인한 유저, 테마, 언어 등과 같이 여러 부분에서 사용되는 데이터들은 context를 사용하여 데이터를 전
역적(global) 변수로 사용할 수 있도록 한다. 중간에 있는 element들이 props를 넘겨받지 않아도 데이터전달이 가능하다.
- 데이터를 받고자 하는 컨포넌트는 useContext Hook을 사용한다
const AuthContext = createContext();
function AuthProvider(props) {
const [user, setUser] = useState(null);
// 로그인
function signIn(username) {
setUser(username);
}
// 로그아웃
function signOut() {
setUser(null);
}
const value = {user, signIn, signOut};
// Provider는 context를 구독하는 컴포넌트들에게 context의 변화를 알리는 역할을 한다
// Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달한다.
return (
<AuthContext.Provider value={value}>
{props.children}
</AuthContext.Provider>
)
}
- AuthProvider에서 넘겨준 value값을 사용하기 위해서는 useContext 훅을 사용한다.
- 삼항연산자로 auth의 user에 값이 있을때는 인삿말과 로그아웃 버튼을, 값이 없을 때는 Not logged in을 출력하도록 한다.
- Outlet으로 자식 컴포넌트들을 출력한다.
function Layout() {
const auth = useContext(AuthContext);
return (
<>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/posts">Posts</Link>
</li>
</ul>
</nav>
{auth.user ? (
<p>
Hi, {auth.user} {" "}
<button onClick={auth.signOut}>
Logout
</button>
</p>
) : (
<p>
Not logged in
</p>
)}
<hr/>
<Outlet />
</>
)
}
- '/post'에 접근하기 전 로그인 창을 띄우기 위해 Post 컴포넌트를 감싸고 있던 AuthRequired에 AuthContext를 사용한다.
function Home() {
return <h1>Home</h1>
}
function Posts() {
return (
<>
<h1>Posts</h1>
<ul>
<li>
<Link to="/post/p1">Post 1</Link>
</li>
<li>
<Link to="/post/p2">Post 2</Link>
</li>
</ul>
</>
)
}
function AuthRequired(props) {
const auth = useContext(AuthContext);
console.log(auth);
function handleSubmit(e) {
e.preventDefault();
const formData = new FormData(e.target);
auth.signIn(formData.get('username'))
}
if(!auth.user) {
return (
<form onSubmit={handleSubmit}>
<h1>Login</h1>
<input type="text" name="username" autoComplete='off' required />
<button type="submit">Login</button>
</form>
)
}
return props.children;
}
function Post() {
const params = useParams();
const postId = params.postId;
return (
<>
<h1>Post</h1>
<p>{postId}</p>
</>
)
}
function NotFound() {
return <h1>404 Not Found</h1>
}
# 서버에서 데이터 가져오기 예시
* useEffect Hook
- 비동기적으로 작동한다.
1 useEffect(effect): effect는 컴포넌트가 실행될 때마다 실행됨
2 useEffect(effect, []): effect는 컴포넌트의 최초 실행시에만 실행됨
3 useEffect(effect, [dep]): effect는 컴포넌트의 최초 실행시, 혹은 dependency가 업데이트 될 때마다 실행됨
- useEffect 간단한 예시
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('lol')
}, []) // []가 있으므로 해당 effect는 최초 한번만 작동한다.
return (
<>
<h1>count: {count}</h1>
<button onClick={() => setCount(count + 1)}>+</button>
</>
)
}
- 데이터 가져오기 예시
서버에서 응답을 주는데 2초가 걸린다고 가정(setTimeout() 사용)
- promise.finally() : Promise가 성공적으로 수행 되었는지 거절되었는지에 관계없이 Promise가 처리 된 후에 코드가 무조건 한 번은 실행됨.
function fetchData() {
const DATA ={
username: 'bunny',
image: 'https://#.com',
bio: '안녕하세요 여러분!'
}
// 2초 뒤 DATA값을 반환하는 Promise객체
const promise = new Promise((res, rej) => {
setTimeout(() => {
res(DATA)
}, 2000)
})
return promise;
}
function App() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [profile, setProfile] = useState(null);
useEffect(() => {
fetchData()
.then(data => {
setProfile(data)
})
.catch(error => {
setError(error)
})
// fetchData가 성공하든, 실패하든 2초 후 Isloaded값을 true로 변환한다
.finally(() => setIsLoaded(true))
}, [])
if(error) {
return <p>failde to fetch profile</p>
}
// 비동기함수인 fetchData()가 가장 늦게 시작되기 때문에 값을 변환하기 전까지 p태그를 출력한다
if(!isLoaded) {
return <p>fetching profile...</p>
}
return (
<>
<h1>Profile</h1>
<img
src={profile.image}
alt={profile.username}
style={{
width:'100px',
height: '100px',
objectFit: 'cover',
borderRadius: '50%'
}}
/>
<h3>{profile.username}</h3>
<p>{profile.bio}</p>
</>
)
}
'JavaScript' 카테고리의 다른 글
패스트캠퍼스 JavaScript 코딩테스트 강의 1주차 (0) | 2023.04.22 |
---|---|
[React] tailwind 설치하기 (0) | 2023.01.29 |
[React] 컴포넌트와 props (0) | 2023.01.20 |
React 1일 (0) | 2023.01.17 |
JS 10일차 (0) | 2023.01.03 |
댓글