[In React] How to display something like "All ○ stories" that is often found on novel posting sites

Introduction

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.

What I wanted to achieve

Reason for struggling

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).

Environment, premise, etc.

environment

Premise

Rails side

スクリーンショット 2020-09-21 16.29.12.png

React side

Rails side code

routing

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

controller

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

React side code

//hierarchy

//src
//  ├ Home.js
//  ├ Series.js
//  ├ ItemCount.js
//  └ useFetch.js

useFetch custom hook

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.
    }
}

Home component

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

Series component

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

ItemCount component

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

Recommended Posts

[In React] How to display something like "All ○ stories" that is often found on novel posting sites
How to run npm install on all projects in Lerna
How to display a graph in Ruby on Rails (LazyHighChart)