This time, I had a lot of trouble (it took 3 days) to display "All ○ stories" that are often found on novel posting sites on my own app, so I would like to leave it as a commandment.
We prepared two models, one that has a folder-like role called " series "
and the other that stores multiple models in the series called " item "
(one-to-many relationship).
Display all "series" on the root page
, get all the" items "owned by that series ", count the total number, and display in the form of
all ~ I want to.
root page
. Normally, if you want to get the items owned by each series
, for example, if the URL is"/ series / 104"
, you get the series parameter (104 in this case) and rely on that parameter for the item. To get.That's why I was thinking about how to get the parameters of each series on the root page where the parameters do not exist (it was a simple story when I think about it now).
front end
React (v16.8 and above)
React Hooks (use custom hooks)
axios
Back end
Rails (5.2 series)
Controller
Api::V1::SeriesController
Create an action that returns the entire series and an action that returns the item count with this controller.
Routing
Root: " / "
→ " api / v1 / series # index "
Item acquisition: " api / v1 / item_count /: series_id "
→ " api / v1 / series # item_count "
Home component
: Acquires all series and gives a component called Series the role of passing each data in order.Series component
: Display each series with this component.routes.rb
Rails.application.routes.draw do
#root
root to: 'api/v1/series#index'
#Item count
get 'api/v1/item_count/:id', to: 'api/v1/series#item_count'
end
app/controller/api/v1/series_controller.rb
class Api::V1::SeriesController < ApplicationController
# item_Call back the series obtained from the parameter to the count action
before_action :set_series, only: [:item_count]
def index
@series =Series.all
render json: {
status: 200,
series: @series,
keyword: "index_of_series" #Used on React side
}
end
def item_count
@items = @series.items.all #Get items associated with a series
@items_count = @items.count #Count the total number of items
render json: {
status: 200,
item_count: @item_count, #Send count as JSON to React
keyword: "item_count" #Used on React side
}
end
private
#Get the series by relying on the parameters
def set_series
@series = Series.find(params[:id])
end
end
//hierarchy
//src
// ├ Home.js
// ├ Series.js
// ├ ItemCount.js
// └ useFetch.js
src/useFetch.js
import { useState, useEffect } from "react"
import axios from 'axios'
//Use is required at the beginning of the sentence for custom hooks
//Pass method and url as arguments of useFetch
//This is used to communicate with Rails in the Home and ItemCount components
//To specify HTTP requests and routing
export default function useFetch({method, url}) {
//Definition of initial value.
const [items, setItems] = useState("")
useEffect(() => {
const getItems = () => {
//From the Home / ItemCount component with the method and url here
//You will substitute the method and routing that will be sent.
axios[method](url)
.then(response => {
let res = response.data
let ok = res.status === 200
//Acquired all series
//The keyword specified on the Rails side is used here.
//That is how it is distinguished from the count.
if (ok && key === 'index_of_series') {
setItems({ ...res.series })
//Get the total number of items per series
} else if (ok && key === 'item_count') {
setItems(res.item_count)
}
})
.catch(error => console.log(error))
}
getItems()
}, [method, url, items])
return {
items //Make the items variable available to other components.
}
}
src/Home.js
import React from 'react'
import Series from './Series'
import useFetch from './useFetch'
function Home() {
//Here, we are receiving the series data obtained by Rails from useFetch.
//method is get and url is Rails root URL. This will
//UseFetch sends a request to Rails route routing
//After that, the data received from Rails is stored in items.
const { items } = useFetch({
method: "get",
url: 'http://localhost:3001'
})
return (
<div>
{/* Object.keys()Use the method to display the items sent in JSON*/}
{/*It is passed to the Series component one by one by loop processing.*/}
{/*JSON is{ {...}, {...}, {...} }Assumed to be something like*/}
{Object.keys(items).map(key => (
<Series key={key} items={items[key]} />
))}
</div>
)
}
export default Home
src/Series.js
import React from 'react'
import ItemCount from './ItemCount'
function Series(props) {
//The id of each series is obtained here, relying on the props sent from Home.
//By using this id as a parameter, you can access the items owned by each series.
const seriesId = props.items.id
const seriesTitle = props.items.title
return (
<div>
<div>{seriesTitle}</div>
{/*Pass the id of the series to the ItemCount component.*/}
<ItemCount {...props} seriesId={seriesId} />
</div>
)
}
export default Series
src/ItemCount.js
import React from 'react'
import useFetch from './useFetch'
function SeriesCount(props) {
//Communicate with Rails using useFetch.
//method is get, url is Rails`api/v1/item_count/${props.seriesId}`Is specified.
//By inserting the id of each series that comes from the Series component into the id part,
//Rails"api/v1/item_count/:id"A request is sent to the routing
//After that, it receives the count number of items in each series from Rails, and finally stores it in items.
const { items } = useFetch({
method: 'get',
url: `http://localhost:3001/api/v1/item_count/${props.seriesId} `
})
return (
<div>
{/*Render the total number of items sent by Rails here.*/}
(This series is all{items}I own 1 item)
</div>
)
}
export default SeriesCount