FullStackOpen Part7-a React Router
Application navigation structure
こんな感じにトップにルーティング用のリンクを用意したとする
以下のようにすれば確かに実現できるが、クリックしたとしてもひとつのページの中でとどまるため、戻るボタンなどが使えない。
なんらかのURLの変化が起きるようにしたい。
import { useState } from 'react'
import ReactDOM from 'react-dom/client'
const Home = () => (
<div> <h2>TKTL notes app</h2> </div>
)
const Notes = () => (
<div> <h2>Notes</h2> </div>
)
const Users = () => (
<div> <h2>Users</h2> </div>
)
const App = () => {
const [page, setPage] = useState('home')
const toPage = (page) => (event) => {
event.preventDefault()
setPage(page)
}
const content = () => {
if (page === 'home') {
return <Home />
} else if (page === 'notes') {
return <Notes />
} else if (page === 'users') {
return <Users />
}
}
const padding = {
padding: 5
}
return (
<div>
<div>
<a href="" onClick={toPage('home')} style={padding}>
home
</a>
<a href="" onClick={toPage('notes')} style={padding}>
notes
</a>
<a href="" onClick={toPage('users')} style={padding}>
users
</a>
</div>
{content()}
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
React Router
この問題を解決するのがreact-router-dom
npm install react-router-dom
こんな感じで使う。
Routerの中にRoutesとLinkを定義して、実際のコンテンツはRouteの中に入れる
末尾が非常に見間違えやすいので注意
import {
BrowserRouter as Router,
Routes, Route, Link
} from 'react-router-dom'
const App = () => {
const padding = {
padding: 5
}
return (
<Router>
<div>
<Link style={padding} to="/">home</Link>
<Link style={padding} to="/notes">notes</Link>
<Link style={padding} to="/users">users</Link>
</div>
<Routes>
<Route path="/notes" element={<Notes />} />
<Route path="/users" element={<Users />} />
<Route path="/" element={<Home />} />
</Routes>
<div>
<i>Note app, Department of Computer Science 2023</i>
</div>
</Router>
)
}
Parameterized route
ノートの詳細ページへのリンクを作るとこんな感じ
useParamsをインポートしてURLの:idで指定したパラメータを受け取る
import {
// ...
useParams
} from 'react-router-dom'
const Note = ({ notes }) => {
const id = useParams().id
const note = notes.find(n => n.id === Number(id))
return (
<div>
<h2>{note.content}</h2>
<div>{note.user}</div>
<div><strong>{note.important ? 'important' : ''}</strong></div>
</div>
)
}
Router側は以下のようにする
path中で:idとしておく
<Router>
// ...
<Routes>
<Route path="/notes/:id" element={<Note notes={notes} />} />
<Route path="/notes" element={<Notes notes={notes} />} />
<Route path="/users" element={user ? <Users /> : <Navigate replace to="/login" />} />
<Route path="/login" element={<Login onLogin={login} />} />
<Route path="/" element={<Home />} />
</Routes>
</Router>
useNavigate
useNavigateはReact Router中でイベントからURLを遷移させたいときなどに使う
例えばログインボタンを押した後にホームに遷移するようにするには以下のようにする
ログイン用のリンクか、それともログイン中であればユーザー名を表示する
<Router>
<div>
<Link style={padding} to="/">home</Link>
<Link style={padding} to="/notes">notes</Link>
<Link style={padding} to="/users">users</Link>
{user
? <em>{user} logged in</em>
: <Link style={padding} to="/login">login</Link>
}
</div>
// ...
</Router>
import {
// ...
useNavigate
} from 'react-router-dom'
const Login = (props) => {
const navigate = useNavigate()
const onSubmit = (event) => {
event.preventDefault()
props.onLogin('mluukkai')
navigate('/')
}
return (
<div>
<h2>login</h2>
<form onSubmit={onSubmit}>
<div>
username: <input />
</div>
<div>
password: <input type='password' />
</div>
<button type="submit">login</button>
</form>
</div>
)
}
onSubmitを押したら、'/'ホームに遷移するようにしている
const navigate = useNavigate()を使用
redirect
<Navigate replace to='/login' />を使用してリダイレクトさせることができる
例:
const App = () => {
const [notes, setNotes] = useState([
// ...
])
const [user, setUser] = useState(null)
const login = (user) => {
setUser(user)
}
const padding = {
padding: 5
}
return (
<div>
<Router>
<div>
<Link style={padding} to="/">home</Link>
<Link style={padding} to="/notes">notes</Link>
<Link style={padding} to="/users">users</Link>
{user
? <em>{user} logged in</em>
: <Link style={padding} to="/login">login</Link>
}
</div>
<Routes>
<Route path="/notes/:id" element={<Note notes={notes} />} />
<Route path="/notes" element={<Notes notes={notes} />} />
<Route path="/users" element={user ? <Users /> : <Navigate replace to="/login" />} />
<Route path="/login" element={<Login onLogin={login} />} />
<Route path="/" element={<Home />} />
</Routes>
</Router>
<footer>
<br />
<em>Note app, Department of Computer Science 2023</em>
</footer>
</div>
)
}
Parameterized route revisited
ノートを個別に表示する際にすべてのnotesをpropsとして渡すのではなく、idに当てはまるものだけ渡すにはuseMatchが使える
import {
// ...
useMatch
} from 'react-router-dom'
const App = () => {
// ...
const match = useMatch('/notes/:id')
const note = match
? notes.find(note => note.id === Number(match.params.id))
: null
return (
<div>
<div>
<Link style={padding} to="/">home</Link>
// ...
</div>
<Routes>
<Route path="/notes/:id" element={<Note note={note} />} />
<Route path="/notes" element={<Notes notes={notes} />} />
<Route path="/users" element={user ? <Users /> : <Navigate replace to="/login" />} />
<Route path="/login" element={<Login onLogin={login} />} />
<Route path="/" element={<Home />} />
</Routes>
<div>
<em>Note app, Department of Computer Science 2023</em>
</div>
</div>
)
}
ただしuseMatchはRouterを定義するコンポーネント中では使えないので、Appの外側にRouterを持っていく
ReactDOM.createRoot(document.getElementById('root')).render(
<Router>
<App />
</Router>
)
この記事が気に入ったらサポートをしてみませんか?