【React로】 소설 투고 사이트 등에 자주 있는 「전 ○ 이야기」같은 것을 표시시키는 방법

18502 단어 React루비Rails후크

소개



이번은 소설 투고 사이트 등에 자주 있는 「전○화」를 자작 어플에서 표시시키는데 상당히 고생했다(3일 걸렸다) 때문에, 계명으로서 남겨 두고 싶습니다.

실현하고 싶은 일


  • 준비한 것은 「シリーズ」 라는 폴더적인 역할을 가지는 모델과 「アイテム」
  • ルートページ 에서 「시리즈」전건을 표시시켜, 그 「シリーズ」が所有する「アイテム」を全て取得し、その総数をカウント 시켜 「全〜件」という形で表示 시키고 싶다.

  • 고생한 이유


  • 표시시키고 싶은 것이 ルートページ 이었기 때문입니다. 보통이라면 各シリーズが所有するアイテム 를 얻으려고 하는 경우, 예를 들어 URL이 "/series/104" 라면, 시리즈의 파라미터(이 경우라면 104)를 취득해, 그 파라미터를 의지해 아이템을 취득합니다.

  • 그래서 그래서, 파라미터가 존재하지 않는 루트 페이지에서 어떻게 각 시리즈의 파라미터를 취득하면 옳은 느낌과 중반 깨어 가면서 생각하고 있었던 것입니다(지금 생각하면 단순한 이야기였습니다)

    환경·전제 등



    환경



  • 프런트 엔드
  • React(v16.8 이상)
  • React Hooks (사용자 정의 후크 사용)
  • axios



  • 백엔드
  • Rails(5.2계)


  • 전제


  • CORS 설정, 모델 생성 등의 과정은 생략합니다.
  • PUMA는 Rails 측 로컬 호스트를 기본적으로 3001로 지정합니다.

  • Rails 측


  • 모델



  • 컨트롤러
  • Api::V1::SeriesController
  • 이 컨트롤러로 시리즈 전건을 돌려주는 액션과 아이템의 카운트를 돌려주는 액션을 작성한다.


  • 라우팅
  • 루트: "/""api/v1/series#index"
  • 아이템 취득: "api/v1/item_count/:series_id""api/v1/series#item_count"


  • React측


  • 준비하는 컴퍼넌트
  • Homeコンポーネント : 시리즈를 전건 취득하고 Series라는 컴퍼넌트에 각 데이터를 순차적으로 전달하는 역할을 갖게 한다.
  • Seriesコンポーネント : 이 컴포넌트로 각 시리즈를 표시시킨다.
  • ItemCountコンポーネント : 각 시리즈가 가지는 아이템의 총수만을 표시시킨다.
  • useFetchカスタムフック : Rails에서 데이터를 가져옵니다.


  • Rails 측 코드



    라우팅



    routes.rb
     Rails.application.routes.draw do
    
      # ルート
      root to: 'api/v1/series#index'
      # アイテムのカウント
      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_countアクションに、パラメータから取得したシリーズをコールバック
        before_action :set_series, only: [:item_count]
    
        def index
            @series =Series.all
            render json: { 
                status: 200,
                series: @series,
                keyword: "index_of_series"  # React側で使う
            }
        end
    
        def item_count
            @items = @series.items.all    # シリーズに関連付けられているアイテムの取得
            @items_count = @items.count    # アイテムの総数をカウント
            render json: {
                status: 200, 
                item_count: @item_count,   # カウントをJSONとしてReactへ送信
                keyword: "item_count"     # React側で使う
            }
        end
    
    
        private
    
            # パラメータを頼りにシリーズを取得
            def set_series
                @series = Series.find(params[:id])
            end
    
    end
    

    React 측 코드


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

    useFetch 사용자 정의 후크



    src/useFetch.js
    
    import { useState, useEffect } from "react"
    import axios from 'axios'
    
    // カスタムフックでは文頭はuseが必須
    // useFetchの引数に、methodとurlを渡す
    // これは、HomeとItemCountコンポーネントにて、Railsとの通信に使う
    // HTTPリクエストと、ルーティングを指定するため
    export default function useFetch({method, url}) {
        // 初期値の定義。
        const [items, setItems] = useState("")
    
        useEffect(() => {
            const getItems = () => {
                // ここのmethodとurlにて、Home・ItemCountコンポーネントから
                // 送られてくるメソッドとルーティングを代入することになる。
                axios[method](url)
                    .then(response => {
                        let res = response.data
                        let ok = res.status === 200
                        // シリーズ全件取得
                        // Rails側で指定したkeywordはここで使う。
                        // そうしてカウントとの区別を付けている。
                        if (ok && key === 'index_of_series') {
                            setItems({ ...res.series })
                        // シリーズごとのアイテムの総数を取得
                        } else if (ok && key === 'item_count') {
                            setItems(res.item_count)
                        }
                    })
                    .catch(error => console.log(error))
            }
            getItems()
        }, [method, url, items])
    
        return {
            items  // items変数を他のコンポーネントで使えるようにする。
        }
    }
    

    Home 구성 요소



    src/Home.js
    import React from 'react'
    
    import Series from './Series'
    import useFetch from './useFetch'
    
    function Home() {
        // ここでは、useFetchからRailsで取得したシリーズのデータを受け取っている。
        // methodはget、urlはRailsのルートのURLを指定。これにより、
        // useFetchからRailsのルートのルーティングへリクエストが送信され、
        // その後Railsから受け取ったデータをitemsへ格納します。
        const { items } = useFetch({
            method: "get",
            url: 'http://localhost:3001'
        })
    
        return (
            <div>
                 {/* Object.keys()メソッドを使い、JSONで送られてくるitemsを */}
                 {/* ループ処理で1個ずつSeriesコンポーネントに渡している。 */}
                 {/* JSONは、{ {...}, {...}, {...} }のようなものであると想定 */}
                  {Object.keys(items).map(key => (
                      <Series key={key} items={items[key]} />
                  ))}
            </div>
        )
    }
    
    export default Home
    

    Series 구성 요소



    src/Series.js
    import React from 'react'
    
    import ItemCount from './ItemCount'
    
    function Series(props) {
        // Homeから送られてくるpropsを頼りに、各シリーズのidをここで取得しています。
        // このidをパラメータとして使うことで、各シリーズの所有するアイテムにアクセスすることができます。
        const seriesId = props.items.id
        const seriesTitle = props.items.title
    
        return (
            <div>
                 <div>{seriesTitle}</div>
                 {/* ItemCountコンポーネントに、シリーズのidを渡す。 */}
                 <ItemCount {...props} seriesId={seriesId} />
            </div>
        )
    }
    
    export default Series
    

    ItemCount 구성 요소



    src/ItemCount.js
    import React from 'react'
    
    import useFetch from './useFetch'
    
    function SeriesCount(props) {
        // useFetchを使いRailsと通信。
        // methodはget、urlはRailsの`api/v1/item_count/${props.seriesId}`を指定。
        // id部分にSeriesコンポーネントから渡ってくる各シリーズのidを嵌め込むことで、
        // Railsの"api/v1/item_count/:id"というルーティングへリクエストが送信され、
        // その後Railsから各シリーズの持つアイテムのカウント数を受け取り、最後にitemsへ格納されます。
        const { items } = useFetch({ 
           method: 'get', 
           url: `http://localhost:3001/api/v1/item_count/${props.seriesId} `
        })
    
        return (
            <div>
                {/* Railsから送られてくるアイテムの総数をここにレンダリングします。 */}
                (このシリーズは全部で {items} 個のアイテムを所有しています)
            </div>
        )
    }
    
    export default SeriesCount
    
    

    좋은 웹페이지 즐겨찾기