Make it to make it

いろいろ作ってアウトプットするブログ

React学習7(機能追加)

外部データから取得して一覧表示して、Like付けたり削除できたりできるところまで。

ファイル構成

.
├── App.js
├── Vidly.jsx
├── components
│   ├── atoms
│   │   └── Like.jsx
│   └── molecules
│       └── Movies.jsx
├── data
│   └── services
│       ├── fakeGenreService.js
│       └── fakeMovieService.js
└── index.js

ファイル中身

Vidly.jsx

import React, { Component } from 'react'
import { getMovies } from './data/services/fakeMovieService'
import Movies from './components/molecules/Movies'

class Vidly extends Component {
  state = {
    movies: getMovies(),
  }

  constructor() {
    super()

    const { movies } = this.state

    this.state.movies = movies.map(movie => {
      movie.liked = false
      return movie
    })
  }

  render() {
    const { movies } = this.state

    return (
      <div className="container">
        <main className="container" role="main">
          <h1>Vidly</h1>
          <Movies
            movies={movies}
            onLike={this.handleLike}
            onDelete={this.handleDelete}
          />
        </main>
      </div>
    )
  }

  handleLike = movie => {
    const likedMovies = [...this.state.movies]
    const movieIndex = likedMovies.findIndex(m => m === movie)

    likedMovies[movieIndex].liked = !likedMovies[movieIndex].liked

    this.setState({
      movies: likedMovies,
    })
  }

  handleDelete = movie => {
    const filteredMovies = [...this.state.movies].filter(m => {
      return m !== movie
    })

    this.setState({
      movies: filteredMovies,
    })
  }
}

export default Vidly

Movies.jsx

import React from 'react'
import Like from '../atoms/Like'

const Movies = ({ movies, onLike, onDelete }) => {
  if (movies.length === 0) return <p>There are no movies!</p>

  return (
    <>
      <p>There are {movies.length} movies!</p>
      <table className="table">
        <thead>
          <tr>
            <th scope="col">id</th>
            <th scope="col">Title</th>
            <th scope="col">Genre</th>
            <th>Like</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          {movies.map((movie, index) => (
            <tr key={index}>
              <th scope="row">{movie._id}</th>
              <td>{movie.title}</td>
              <td>{movie.genre.name}</td>
              <td>
                <span
                  onClick={() => onLike(movie)}
                  style={{ display: 'inline-block', cursor: 'pointer' }}
                >
                  <Like movie={movie} />
                </span>
              </td>
              <td>
                <button
                  onClick={() => onDelete(movie)}
                  type="button"
                  className="btn btn-danger"
                >
                  Delete
                </button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </>
  )
}

export default Movies

Like.jsx

import React from 'react'

const Like = ({ movie }) => {
  return <i className={movie.liked ? 'fas fa-heart' : 'far fa-heart'} />
}

export default Like

fakeMovieService.js

import * as genresAPI from './fakeGenreService'

const movies = [
  {
    _id: '5b21ca3eeb7f6fbccd471815',
    title: 'Terminator',
    genre: { _id: '5b21ca3eeb7f6fbccd471818', name: 'Action' },
    numberInStock: 6,
    dailyRentalRate: 2.5,
    publishDate: '2018-01-03T19:04:28.809Z',
  },
  {
    _id: '5b21ca3eeb7f6fbccd471816',
    title: 'Die Hard',
    genre: { _id: '5b21ca3eeb7f6fbccd471818', name: 'Action' },
    numberInStock: 5,
    dailyRentalRate: 2.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd471817',
    title: 'Get Out',
    genre: { _id: '5b21ca3eeb7f6fbccd471820', name: 'Thriller' },
    numberInStock: 8,
    dailyRentalRate: 3.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd471819',
    title: 'Trip to Italy',
    genre: { _id: '5b21ca3eeb7f6fbccd471814', name: 'Comedy' },
    numberInStock: 7,
    dailyRentalRate: 3.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd47181a',
    title: 'Airplane',
    genre: { _id: '5b21ca3eeb7f6fbccd471814', name: 'Comedy' },
    numberInStock: 7,
    dailyRentalRate: 3.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd47181b',
    title: 'Wedding Crashers',
    genre: { _id: '5b21ca3eeb7f6fbccd471814', name: 'Comedy' },
    numberInStock: 7,
    dailyRentalRate: 3.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd47181e',
    title: 'Gone Girl',
    genre: { _id: '5b21ca3eeb7f6fbccd471820', name: 'Thriller' },
    numberInStock: 7,
    dailyRentalRate: 4.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd47181f',
    title: 'The Sixth Sense',
    genre: { _id: '5b21ca3eeb7f6fbccd471820', name: 'Thriller' },
    numberInStock: 4,
    dailyRentalRate: 3.5,
  },
  {
    _id: '5b21ca3eeb7f6fbccd471821',
    title: 'The Avengers',
    genre: { _id: '5b21ca3eeb7f6fbccd471818', name: 'Action' },
    numberInStock: 7,
    dailyRentalRate: 3.5,
  },
]

export function getMovies() {
  return movies
}

export function getMovie(id) {
  return movies.find(m => m._id === id)
}

export function saveMovie(movie) {
  let movieInDb = movies.find(m => m._id === movie._id) || {}
  movieInDb.name = movie.name
  movieInDb.genre = genresAPI.genres.find(g => g._id === movie.genreId)
  movieInDb.numberInStock = movie.numberInStock
  movieInDb.dailyRentalRate = movie.dailyRentalRate

  if (!movieInDb._id) {
    movieInDb._id = Date.now()
    movies.push(movieInDb)
  }

  return movieInDb
}

export function deleteMovie(id) {
  let movieInDb = movies.find(m => m._id === id)
  movies.splice(movies.indexOf(movieInDb), 1)
  return movieInDb
}

fakeGenreService.js

export const genres = [
  { _id: '5b21ca3eeb7f6fbccd471818', name: 'Action' },
  { _id: '5b21ca3eeb7f6fbccd471814', name: 'Comedy' },
  { _id: '5b21ca3eeb7f6fbccd471820', name: 'Thriller' },
]

export function getGenres() {
  return genres.filter(g => g)
}