これは何?
React+Railsで簡単なタスク管理アプリを作っています。
一覧画面からタスク名のリンクをクリックすると、詳細画面が現れます。
実行環境は以下の通りです。
- Rails 6.0.3
- React 17.0.2
また、今回のディレクトリ構成は以下の通りです。(関係のある箇所だけ表示)
.
├── controllers
│ └── api
│ └── tasks_controller.rb
└── javascript
└── pages
├── Task.jsx
└── Tasks.jsx
Rails側
spaではないRailsのアプリとほぼ変わりありません。/api
配下に置いたコントローラーにindex
(実装については前回の記事をご参照ください)とshow
アクションを記載します。
render json: ...
でJSON形式でビューを描画します。
class Api::TasksController < ApplicationController
def index
@tasks = current_user.tasks
render json: @tasks
end
def show
@task = current_user.tasks.find(params[:id])
render json: @task
end
end
SPA側(一覧表示画面)
タスク一覧表示画面(Tasks
コンポーネント)に実際に書いたコードがこちらです。
import React, {useState} from 'react'
import { Link, withRouter } from 'react-router-dom'
import axios from 'axios'
export const Tasks = withRouter(() => { // ★2解説します
const [tasks, setTasks] = useState([])
React.useEffect(async () =>{
const response = await axios.get('/api/tasks');
setTasks(response.data)
}, [])
return (
<div>
<h1>タスク一覧</h1>
<table>
// 中略
<tbody>
{tasks.map(task => (
<tr key={task.id}>
<td>
<Link to={{ // ★1解説します
pathname: "/spa/tasks/" + task.id,
state: {id: task.id},
}}>{task.name}</Link>
</td>
// 中略
</tr>
))}
</tbody>
</table>
</div>
)
})
react-router と react-router-dom
まず、調べていて迷ったのが、react-router
とreact-router-dom
があるっぽいということ。その点は、こちらの記事が非常に丁寧に解説してくれていました。
要するに、react-router-dom
の方が新しくて使い勝手も良いそうなので、react-router-dom
を使いましょう、とのことでした。
Link to
ポイントの一つ目はこの部分(★1)で、
<Link to={{
pathname: "/spa/tasks/" + task.id,
state: {id: task.id},
}}>{task.name}</Link>
react-router-dom
のLink
という関数で、遷移先や遷移先のページに送るデータなどが指定できます。詳しくは公式のAPIドキュメントがわかりやすかったですが、
上記を指定することで、受取手側のコンポーネントではlocatiton
で以下のような情報が取得できました。
console.log(location)
=> {pathname: "/spa/tasks/1", state: {id: 1}, search: "", hash: "", key: "5ox837"}
withRouter
ただし、上記だけではlocation
のstate
がundefined
になります。調べてみるとwithRouterでラップしてねという回答がたくさん出てきたので、今回も以下のように全体をラップしてみました(★2)。
export const Tasks = withRouter(() => {
// 略
})
公式のドキュメントもみてみると、
withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
(withRouterは、レンダリングされるたびに、更新されたmatch、location、およびhistoryのpropsをラップされたコンポーネントに渡します。)
とのことでした。Functional ComponentでのwithRouter
の使い方は、こちらを参考にしました。
完成
以上で、実装は終わりになります。
まさかShowアクションでここまで調べることになるとは思わなかった...^^
本日はDestroyアクションも試してみたのですが、そちらはまた別の機会に記事にしたいです。
ではでは。