0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React カスタムプロバイダー

Posted at

はじめに

Reactを触り始めて、3日目。。。
備忘もかねて投稿します。

React素人のため、情報、書き方に誤りがあるかもしれません。
その場合はコメント等で指摘していただけると幸いです。

環境

React 18.3.1

概要

カスタムプロバイダーを利用したコンテキスト経由でのデータの公開と、
ステートの管理について把握するためのサンプルです

映画レーティング

ユーザーは、映画の削除、追加、レーティングが行える

Screenshot 2024-11-18 at 22.18.08.png

映画データ

color-data.json

id 映画を識別する一意の値
title 映画名
rating 評価
[
  {
    "id": "0175d1f0-a8c6-41bf-8d02-df5734d829a4",
    "title": "Jurassic Park",
    "rating": 0
  },
  {
    "id": "83c7ba2f-7392-4d7d-9e23-35adbe186046",
    "title": "Star Wars",
    "rating": 0
  },
]

各種コンポーネント

MovieProvider

ステート(映画データ)を保持し、カスタムプロバイダーとして機能する
カスタムプロバイダーはデータの公開、データの変更を可能にする

データ変更用のAPIを公開し、子コンポーネントはこのAPIを利用してデータの変更を行う

import React, { createContext, useState, useContext } from "react";
import movieData from "./data/movie-data.json";

// コンテキストを初期化
const MovieContext = createContext();
// コンテキストのフック
export const useMovies = () => useContext(MovieContext);

export default function MovieProvider({ children }) {
  const [movies, setMovies] = useState(movieData);

  // 子コンポーネントに公開されるAPI
  const addMovie = (title) =>
    setMovies(...movies, {
      id: v4(),
      rating: 0,
      title,
    });

  const removeMovie = (id) =>
    setMovies(movies.filter((movie) => movie.id !== id));

  const rateMovie = (id, rating) =>
    setMovies(
      movies.map((movie) => (movie.id === id ? { ...movie, rating } : movie))
    );

  return (
    <MovieContext.Provider
      value={{ movies, addMovie, removeMovie, rateMovie }}
    >
      {children}
    </MovieContext.Provider>
  );
}

MovieList

MovieProvierで読み込まれた映画データをuseMovies()より参照

import React from "react";
import { useMovies } from "./MovieProvider";
import Movie from "./Movie";

export default function MovieList() {
  const { movies } = useMovies();
  if (!movies.length) return <div>No Movie Listed.</div>;
  return (
    <div>
      {movies.map((movie) => (
        <Movie key={movie.id} {...movie} />
      ))}
    </div>
  );
}

Movie

Providerで定義された各APIを参照、利用しデータの変更、更新を行う

import React from "react";
import { useMovies } from "./MovieProvider";
import StarRating from "../StarRating";

export default function Movie({ id, title, rating }) {
  const { rateMovie, removeMovie } = useMovies();
  return (
    <section>
      <h1>{title}</h1>

      <StarRating
        selectedStars={rating}
        onRate={(rating) => rateMovie(id, rating)}
      />
      <button onClick={() => removeMovie(id)}>Delete</button>
    </section>
  );
}

StarRating

import React, { useState } from "react";
import Star from "./Stars.js";

export default function StarRating({
  totalStars = 5,
  selectedStars = 0,
  onRate = (f) => f,
}) {
  return (
    <>
      {[...Array(totalStars)].map((n, i) => (
        <Star
          key={i}
          selected={selectedStars > i}
          onSelect={() => onRate(i + 1)}
        />
      ))}
      <p>
        {selectedStars} of {totalStars} stars
      </p>
    </>
  );
}

Star

import React from "react";
import { FaStar } from "react-icons/fa";

export default function Star({ selected = false, onSelect = (f) => f }) {
  return <FaStar color={selected ? "red" : "grey"} onClick={onSelect} />;
}

AddMovieForm

Providerで定義された各APIを参照、利用し新規データを追加する

import React from "react";
import { useInput } from "../hook";
import { useMovies } from "./MovieProvider";

export default function AddMovieForm() {
  const [titleProps, resetTitle] = useInput("");
  const { addMovie } = useMovies();

  const submit = (e) => {
    e.preventDefault();
    addMovie(titleProps.value);
    resetTitle();
  };

  return (
    <form onSubmit={submit}>
      <input
        {...titleProps}
        type="text"
        placeholder="Movie title..."
        required
      />
      <button>ADD</button>
    </form>
  );
}

App

メインコンポーネント

import MovieList from "./movie/MovieList.js";
import AddMovieForm from "./movie/AddMovieForm.js";

function App() {
  return (
    <>
      <AddMovieForm />
      <MovieList />
    </>
  );
}

index.js

メインコンポーネントAppを、
カスタムプロバイダーMovieProviderでラップすることにより、
その配下のコンポーネントからmovieDataおよび、各種データ変更用のAPIにアクセスできる

import React, { createContext } from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import MovieProvider from "./movie/MovieProvider";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <MovieProvider>
      <App />
    </MovieProvider>
  </React.StrictMode>
);

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?