こんにちは、KOUKIです。
GolangとReactでMovie Appの開発をしています。
今回は、Movieデータに付随してついてくるGeres(ジャンル)データを処理していきましょう。
尚、Udemyの「Working with React and Go (Golang)」を参考にしているので、よかったら受講してみてください。
前回
GolangとReactのアプリケーション連携方法を紹介しました。
事前準備
フォルダ/ファイル
1 2 3 |
# ファイル名の変更 mv go-movies/src/components/Categories.tsx go-movies/src/components/Genres.tsx touch go-movies/src/components/OneGenre.tsx |
Genresコンポーネント
以前、「Categories」で作成していたコンポーネントを「Genres」で作り直します。
コンポーネント名の変更
1 2 3 4 5 6 7 |
// components/Genres.tsx const Genres = (props: any) => { return <h2>Genres</h2> } export default Genres |
変更の適用
この変更に伴い、App.tsxも修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// src/App.tsx ... export default function App() { return ( ... <Route exact path="/genres"> // genresへ <Genres /> </Route> .... </Router> ) } |

Genres APIの実装
Genresデータを取得するAPIを実装します。
DB処理
全てのGenresデータを取得するGenresAllメソッドを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// models/movies-db.go func (m *DBModel) GenresAll() ([]*Genre, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, genre_name, created_at, updated_at from genres order by genre_name` rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var genres []*Genre for rows.Next() { var g Genre err := rows.Scan( &g.ID, &g.GenreName, &g.CreatedAt, &g.UpdatedAt, ) if err != nil { return nil, err } genres = append(genres, &g) } return genres, nil } |
ハンドラーの追加
リクエストを処理するためのハンドラーを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// cmd/api/movie-handlers.go func (app *application) getAllGenres(w http.ResponseWriter, r *http.Request) { genres, err := app.models.DB.GenresAll() if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, genres, "genres") if err != nil { app.errorJSON(w, err) return } } |
ルートの追加
Genres APIへアクセスするためのルートを実装します。
1 2 3 4 5 6 7 8 9 10 11 |
// cmd/api/routes.go ... func (app *application) routes() http.Handler { ... router.HandlerFunc(http.MethodGet, "/v1/genres", app.getAllGenres) return app.enableCORS(router) } |
検証
「docker-compose up」でコンテナを立ち上げてください。
そして、以下のパラメータで検証をします。
- URL: http://localhost:4000/v1/genres
- 形式: GET


idが出力されてませんね。
Genreモデル(golang側)を修正します。
1 2 3 4 5 6 7 8 9 |
// models/models.go type Genre struct { // ID int `json:"-"` ID int `json:"id"` GenreName string `json:"genre_name"` CreatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"` } |
jsonタグを変更したので、これでidが表示されるようになります。

Genresリストの表示
ここからは、Reactの実装になります。
Genresモデル
Genresモデル(React側)を実装します。
1 2 3 4 5 |
// models/movie.ts export interface Genres { id: number genre_name: string } |
リストの表示
Genres APIからデータを取得し、それをリスト化しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
// components/Genres.tsx import { useState, useEffect, Fragment } from 'react' import { Link } from 'react-router-dom' import axios from 'axios' import { Genre } from '../models/movie' const Genres = (props: any) => { const [genres, setGenres] = useState<Genre[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('genres') .then((response) => { console.log(response.data.genres) setGenres(response.data.genres) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Genres</h2> <ul> {genres.map((m) => { return ( <li key={m.id}> <Link to={`/genre/${m.id}`}> {m.genre_name} </Link> </li> ) })} </ul> </Fragment> ) } export default Genres |
検証
ブラウザから「http://localhost:3000/genres」にアクセスします。

APIの追加
GoのAPIを一つ追加します。
Allメソッドの修正
Genre IDをキーにMovieデータを取得できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// models/movies-db.go // genreパラメータ追加 func (m *DBModel) All(genre ...int) ([]*Movie, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() where := "" // where句追加 if len(genre) > 0 { where = fmt.Sprintf( "where id in (select movie_id from movies_genres where genre_id = %d)", genre[0]) } query := fmt.Sprintf( // where句を検索に `select id, title, description, year, release_date, rating, runtime, mpaa_rating, created_at, updated_at from movies %s order by title`, where) ... } |
ハンドラー
ハンドラーを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// cmd/api/movie-handlers.go ... func (app *application) getAllMoviesByGenres(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) genreID, err := strconv.Atoi(params.ByName("genre_id")) if err != nil { app.errorJSON(w, err) return } movies, err := app.models.DB.All(genreID) if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, movies, "movies") if err != nil { app.errorJSON(w, err) return } } |
ルート
先ほど実装したハンドラーへリクエストを飛ばすために、ルートも追加しましょう。
1 2 3 4 5 6 7 8 |
// cmd/api/routes.go ... func (app *application) routes() http.Handler { ... router.HandlerFunc(http.MethodGet, "/v1/movies/:genre_id", app.getAllMoviesByGenres) ... } |
検証
下記のパラメータで、検証をしましょう。
- URL: http://localhost:4000/v1/movies/1
- 形式: GET

OKですね。
Genreに紐づくMovieデータの表示
先ほど取得したMoviesデータをページに表示します。
コンポーネント
コンポーネントを実装しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// components/OneGenre.tsx import { useState, useEffect, Fragment } from 'react' import { Link } from 'react-router-dom' import { Movie } from '../models/movie' import axios from 'axios' const OneGenre = (props: any) => { const [movies, setMovies] = useState<Movie[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('movies/' + props.match.params.id) .then((response) => { setMovies(response.data.movies) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (!movies) { setMovies([]) } if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Genre: </h2> <div className="list-group"> {movies.map((m, index) => { return ( <Link to={`/movies/${m.id}`} key={index} className="list-group-item list-group-item-action"> {m.title} </Link> ) })} </div> </Fragment> ) } export default OneGenre |
ルートの追加
OneGenreコンポーネントへのルートを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// src/App.tsx ... import OneGenre from './components/OneGenre' export default function App() { return ( .... <div className="col-md-10"> <Switch> <Route path="/movies/:id" component={OneMovie} /> <Route path="/movies"> <Movies /> </Route> <Route path="/genre/:id" component={OneGenre} /> // 追加 ... </div> </div> </Router> ) } |
検証
私の環境では、genre_id=1のデータがMovieデータに紐づいています。
ブラウザからデータを取得できるか検証してみましょう。※データが取得できない場合は、何も表示されません。
Genreのタイトル
個別ページのGenreに飛んだ時、ActionやComedyなどのタイトルも表示できるようにしましょう。
Linkパラメータの変更
Documentを読むと、Linkのtoにはオブジェクトを渡すことができるようです。オブジェクトのstateパラメータに、genre_nameを渡してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// components/Genres.tsx ... const Genres = (props: any) => { ... return ( <Fragment> <h2>Genres</h2> <ul> {genres.map((m) => { return ( <li key={m.id}> <Link to={{ pathname: `/genre/${m.id}`, state: {genreName: m.genre_name}, // 変更 }}> {m.genre_name} </Link> </li> ) })} </ul> </Fragment> ) } export default Genres |
props
これを遷移先であるOneGenreコンポーネントで受け取ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// components/OneGenre.tsx ... const OneGenre = (props: any) => { ... const [genreName, setGenreName] = useState("") useEffect(() => { ( async () => { await axios.get('movies/' + props.match.params.id) .then((response) => { setMovies(response.data.movies) setIsLoaded(true) setGenreName(props.location.state.genreName) // 追加 }) .catch((err) => { setError(err.message) }) } )() }, []) .... return ( <Fragment> <h2>Genre: {genreName}</h2> // 追加 ... </Fragment> ) } export default OneGenre |
検証
画面を表示してみましょう。

「Genre: Drama」と表示されたので成功です!
スタイリングの変更
長くなりましたが、最後にリファクタリングをしてお終いにしましょう。
Movieリスト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// components/Movies.tsx ... const Movies = () => { ... return ( <Fragment> <h2>Choose a movie</h2> <div className="list-group"> {movies.map((m) => { return ( <Link key={m.id} to={`/movies/${m.id}`} className="list-group-item list-group-item-action" > {m.title} </Link> ) })} </div> </Fragment> ) } export default Movies |

Genresリスト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// components/Genres.tsx ... return ( <Fragment> <h2>Genres</h2> <div className="list-group"> {genres.map((m) => { return ( <Link key={m.id} className="list-group-item list-group-item-action" to={{ pathname: `/genre/${m.id}`, state: {genreName: m.genre_name}, }}> {m.genre_name} </Link> ) })} </div> </Fragment> ) } export default Genres |

次回
次回は、ReactのFormを実装します。
記事まとめ
参考書籍
ソースコード
routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// cmd/api/routes.go package main import ( "net/http" "github.com/julienschmidt/httprouter" ) func (app *application) routes() http.Handler { router := httprouter.New() router.HandlerFunc(http.MethodGet, "/status", app.statusHandler) router.HandlerFunc(http.MethodGet, "/v1/movie/:id", app.getOneMovie) router.HandlerFunc(http.MethodGet, "/v1/movies", app.getAllMovie) router.HandlerFunc(http.MethodGet, "/v1/movies/:genre_id", app.getAllMoviesByGenres) router.HandlerFunc(http.MethodGet, "/v1/genres", app.getAllGenres) return app.enableCORS(router) } |
models.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
// models/models.go package models import ( "database/sql" "time" ) // Models is the wrapper for database type Models struct { DB DBModel } // NewModels returns models with db pool func NewModels(db *sql.DB) Models { return Models{ DB: DBModel{DB: db}, } } // Movie is the type for movies type Movie struct { ID int `json:"id"` Title string `json:"title"` Description string `json:"description"` Year int `json:"year"` ReleaseDate time.Time `json:"release_date"` Runtime int `json:"runtime"` Rating int `json:"rating"` MPAARating string `json:"mpaa_rating"` CreatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"` MovieGenre map[int]string `json:"genres"` } // Genre is the type for genre type Genre struct { // ID int `json:"-"` ID int `json:"id"` GenreName string `json:"genre_name"` CreatedAt time.Time `json:"-"` UpdatedAt time.Time `json:"-"` } // MovieGenre is thee type for movie genre type MovieGenre struct { ID int `json:"-"` MovieID string `json:"-"` GenreID string `json:"-` Genre Genre `json:"genre"` CreatedAt time.Time `json:"-"` UpdateAt time.Time `json:"-"` } |
movies-db.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
// models/movies-db.go package models import ( "context" "database/sql" "fmt" "time" ) type DBModel struct { DB *sql.DB } // Get retuns one movie and error, if any func (m *DBModel) Get(id int) (*Movie, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, title, description, year, release_date, rating, runtime, mpaa_rating, created_at, updated_at from movies where id = $1` row := m.DB.QueryRowContext(ctx, query, id) var movie Movie err := row.Scan( &movie.ID, &movie.Title, &movie.Description, &movie.Year, &movie.ReleaseDate, &movie.Rating, &movie.Runtime, &movie.MPAARating, &movie.CreatedAt, &movie.UpdatedAt, ) if err != nil { return nil, err } // get genres, if any query = `select mg.id, mg.movie_id, mg.genre_id, g.genre_name from movies_genres mg left join genres g on (g.id = mg.genre_id) where mg.movie_id = $1` rows, _ := m.DB.QueryContext(ctx, query, id) defer rows.Close() genres := make(map[int]string) for rows.Next() { var mg MovieGenre err := rows.Scan( &mg.ID, &mg.MovieID, &mg.GenreID, &mg.Genre.GenreName, ) if err != nil { return nil, err } genres[mg.ID] = mg.Genre.GenreName } movie.MovieGenre = genres return &movie, nil } // All retuns all movies and error, if any func (m *DBModel) All(genre ...int) ([]*Movie, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() where := "" if len(genre) > 0 { where = fmt.Sprintf( "where id in (select movie_id from movies_genres where genre_id = %d)", genre[0]) } query := fmt.Sprintf( `select id, title, description, year, release_date, rating, runtime, mpaa_rating, created_at, updated_at from movies %s order by title`, where) rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var movies []*Movie for rows.Next() { var movie Movie err := rows.Scan( &movie.ID, &movie.Title, &movie.Description, &movie.Year, &movie.ReleaseDate, &movie.Rating, &movie.Runtime, &movie.MPAARating, &movie.CreatedAt, &movie.UpdatedAt, ) if err != nil { return nil, err } genreQuery := `select mg.id, mg.movie_id, mg.genre_id, g.genre_name from movies_genres mg left join genres g on (g.id = mg.genre_id) where mg.movie_id = $1` genreRows, _ := m.DB.QueryContext(ctx, genreQuery, movie.ID) genres := make(map[int]string) for genreRows.Next() { var mg MovieGenre err := genreRows.Scan( &mg.ID, &mg.MovieID, &mg.GenreID, &mg.Genre.GenreName, ) if err != nil { return nil, err } genres[mg.ID] = mg.Genre.GenreName } genreRows.Close() movie.MovieGenre = genres movies = append(movies, &movie) } return movies, nil } func (m *DBModel) GenresAll() ([]*Genre, error) { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() query := `select id, genre_name, created_at, updated_at from genres order by genre_name` rows, err := m.DB.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var genres []*Genre for rows.Next() { var g Genre err := rows.Scan( &g.ID, &g.GenreName, &g.CreatedAt, &g.UpdatedAt, ) if err != nil { return nil, err } genres = append(genres, &g) } return genres, nil } |
movie-handlers.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// cmd/api/movie-handlers.go package main import ( "errors" "net/http" "strconv" "github.com/julienschmidt/httprouter" ) func (app *application) getOneMovie(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) // urlからidを抜きだす id, err := strconv.Atoi(params.ByName("id")) if err != nil { app.logger.Print(errors.New("invalid id parameter")) app.errorJSON(w, err) return } movie, err := app.models.DB.Get(id) err = app.writeJSON(w, http.StatusOK, movie, "movie") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllMovie(w http.ResponseWriter, r *http.Request) { movies, err := app.models.DB.All() if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, movies, "movies") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllGenres(w http.ResponseWriter, r *http.Request) { genres, err := app.models.DB.GenresAll() if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, genres, "genres") if err != nil { app.errorJSON(w, err) return } } func (app *application) getAllMoviesByGenres(w http.ResponseWriter, r *http.Request) { params := httprouter.ParamsFromContext(r.Context()) genreID, err := strconv.Atoi(params.ByName("genre_id")) if err != nil { app.errorJSON(w, err) return } movies, err := app.models.DB.All(genreID) if err != nil { app.errorJSON(w, err) return } err = app.writeJSON(w, http.StatusOK, movies, "movies") if err != nil { app.errorJSON(w, err) return } } |
App.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
// src/App.tsx import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom' import Home from './components/Home' import Movies from './components/Movies' import Admin from './components/Admin' import Genres from './components/Genres' import OneMovie from './components/OneMovie' import OneGenre from './components/OneGenre' export default function App() { return ( <Router> <div className="container"> <div className="row"> <h1 className="mt-3"> Go Watch a Movie! </h1> <hr className="mb-3" /> </div> <div className="row"> <div className="col-md-2"> <nav> <ul className="list-group"> <li className="list-group-item"> <Link to="/">Home</Link> </li> <li className="list-group-item"> <Link to="/movies">Movies</Link> </li> <li className="list-group-item"> <Link to="/genres">Genres</Link> </li> <li className="list-group-item"> <Link to="/admin">Manage Catalog</Link> </li> </ul> </nav> </div> <div className="col-md-10"> <Switch> <Route path="/movies/:id" component={OneMovie} /> <Route path="/movies"> <Movies /> </Route> <Route path="/genre/:id" component={OneGenre} /> <Route exact path="/genres"> <Genres /> </Route> <Route path="/admin"> <Admin /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </div> </div> </Router> ) } |
movie.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// models/movie.ts export interface Movie { id: number title: string description: string year: number release_date: string runtime: number rating: number mpaa_rating: string genres: Array<string> } export interface Genre { id: number genre_name: string } |
Movies.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
// components/Movies.tsx import { Link } from 'react-router-dom' import { useEffect, useState, Fragment } from 'react' import { Movie } from '../models/movie' import axios from 'axios' const Movies = () => { const [movies, setMovies] = useState<Movie[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('movies') .then((response) => { setMovies(response.data.movies) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Choose a movie</h2> <div className="list-group"> {movies.map((m) => { return ( <Link key={m.id} to={`/movies/${m.id}`} className="list-group-item list-group-item-action" > {m.title} </Link> ) })} </div> </Fragment> ) } export default Movies |
Genres.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// components/Genres.tsx import { useState, useEffect, Fragment } from 'react' import { Link } from 'react-router-dom' import axios from 'axios' import { Genre } from '../models/movie' const Genres = (props: any) => { const [genres, setGenres] = useState<Genre[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") useEffect(() => { ( async () => { await axios.get('genres') .then((response) => { setGenres(response.data.genres) setIsLoaded(true) }) .catch((err) => { setError(err.message) }) } )() }, []) if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Genres</h2> <div className="list-group"> {genres.map((m) => { return ( <Link key={m.id} className="list-group-item list-group-item-action" to={{ pathname: `/genre/${m.id}`, state: {genreName: m.genre_name}, }}> {m.genre_name} </Link> ) })} </div> </Fragment> ) } export default Genres |
OneGenre.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
// components/OneGenre.tsx import { useState, useEffect, Fragment } from 'react' import { Link } from 'react-router-dom' import { Movie } from '../models/movie' import axios from 'axios' const OneGenre = (props: any) => { const [movies, setMovies] = useState<Movie[]>([]) const [isLoaded, setIsLoaded] = useState(false) const [error, setError] = useState("") const [genreName, setGenreName] = useState("") useEffect(() => { ( async () => { await axios.get('movies/' + props.match.params.id) .then((response) => { setMovies(response.data.movies) setIsLoaded(true) setGenreName(props.location.state.genreName) }) .catch((err) => { setError(err.message) }) } )() }, []) if (!movies) { setMovies([]) } if (error) { return ( <div>Error: {error}</div> ) } else if (!isLoaded) { return ( <p>Loading...</p> ) } return ( <Fragment> <h2>Genre: {genreName}</h2> <div className="list-group"> {movies.map((m, index) => { return ( <Link to={`/movies/${m.id}`} key={index} className="list-group-item list-group-item-action"> {m.title} </Link> ) })} </div> </Fragment> ) } export default OneGenre |
コメントを残す
コメントを投稿するにはログインしてください。