1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ざっくりReact Hooks入門③

Last updated at Posted at 2021-11-11

はじめに

今回はReact Hooksについてのまとめ記事の最終回になります。

useReducer

stateとdispatch(actionを送信する関数)を返すフックです。
このフックを利用すれば、コンポーネント内でstate管理ができます。
そのためuseStateと似たような機能を持つフックです。

構文
const [state, dispatch] = useReducer(reducer, stateの初期値);
戻り値のdispatchにactionを渡すとstateが更新されます。
引数のreducerによって更新のされ方が変わります。

actionとは...

アクション(何が起きたか)ととそれに付随する情報を持つオブジェクトのこと。

const action = {
  type: 'ADD_TODO', // アクションタイプ
  text: 'Learning React' // 付随情報
};

// useReducerの返り値のdispatchにactionを渡すと、actionが送信さてreducerが実行される
// actionを送信することを「actionをdispatchすると言います」
dispatch(action);

reducerとは...

現在のstateとactionを受け取り、actionに応じて更新したstateを返す関数。
actionをdispatchするとreducerが実行される。

// アクションタイプがADD_TODOのactionがdispatchされた時、stateを更新して返す
// useReducerの第一引数にこのようなものを渡す想定
function reducer(state, action) {
  switch(action.type) {
    case 'ADD_TODO':
      return [...state, [{ text: action.text, completed:false }]];
    default:
      return state;
  }
}

使用例

import React, { useReducer } from 'react';

// 現在の state と action を受け取り、action に応じて更新した state を返す関数
function reducer(state, action) {
  switch (action.type) {
    case 'INCEREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'RESET':
      return { count: 0 };
    default:
      return state;
  }
}

export default function App() {
  // useReducerの第2引数に { count: 0 } を渡しているので、state の初期値は { count: 0 }
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <>
      <p>count: {state.count}</p>

      {/* { type: 'DECREMENT' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>

      {/* { type: 'INCEREMENT' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'INCEREMENT' })}>+</button>

      {/* { type: 'RESET' } という action を送信する */}
      <button onClick={() => dispatch({ type: 'RESET' })}>reset</button>
    </>
  );
}

useReducerとuseStateとの使い分け

複雑なstateを扱う時はuseReducerを利用した方が良いです。
それ以外は基本useStateで問題ないです。

「複雑なstate」というのは、下記のように別のstateに依存しているstateを扱う場合を想定しています。
・stateを更新するために、別のstateを参照する必要がある
・stateを更新したら、別のstateも一緒に更新する必要がある
・stateを更新するロジックが複雑

useContext

ReactにおけるContextとは...

以下のいずれかを指している
①Propsを利用せずに様々な階層のコンポーネントに値を共有するReactの仕組み
②Propsを利用せずに様々な階層のコンポーネントに値を共有するReactのAPI
③Contextオブジェクトのこと
④Contextオブジェクトの値のこと

今回は①の意味で話を進めます。

Contextを使用するには...

以下が必要
①Contextオブジェクト
->React.createContextの戻り値を指す
②Provider
->Contextオブジェクトが保持しているコンポーネントを指す(Providerコンポーネントとも呼ぶ)
③Consumer
->Contextオブジェクトから値を首都区しているコンポーネントを指す(Consumerコンポーネントとも呼ぶ)

importReact,{createContext,useContext}from'react';

// ①Contextオブジェクト
const MyContext = createContext();
// ③MyContextから値を取得しているのでConsumerである
functionChild1(){
  //<MyContext.Providervalue={name}>のnameは'React'なので、textの値は'React'になる。 
  const text = useContext(MyContext);
  return <h1>{text}</h1>;
}
// MyContextから値を取得していないのでConsumerではない
functionChild2(){
  return <h2>NotConsumer</h2>;
}

export defaultfunctionApp(){
  constname='React';
  return(
    {/*②Provider。valueプロパティの値をConsumerに共有する。*/}
    <MyContext.Providervalue={name}>
      <Child1/>
      <Child2/>
    </MyContext.Provider>
  );
}

Contextを使う理由

①グローバルなstateを作成できるため
②Prop drilling問題(バケツリレー問題)を解決できるため

useContextとは

Contextオブジェクトから値を取得するフック。
Providerから共有される値を取得するために必要。
簡単にいうと、親からPropsで渡されていないのに、Contextに収容されているデータへよりシンプルにアクセスできる機能です。

構文
const Context オブジェクトの値 = useContext(Context オブジェクト);
Contextオブジェクトから取得できる値はContextオブジェクトが保持しているProviderのvalueプロパティの値になります。

使用例

import React, { useState, useContext, createContext } from "react";

// Context オブジェクト
const MyContext = createContext();

export default function App() {
  const [count, setCount] = useState(0);
  const value = {
    name: "soarflat",
    handleClick: () => setCount((count) => count + 1)
  };

  return (
    <div>
      <p>count: {count}</p>
      {/* Provider。value プロパティの値を共有する。 */}
      <MyContext.Provider value={value}>
        <Child />
      </MyContext.Provider>
    </div>
  );
}

function Child() {
  return <GrandChild />;
}

function GrandChild() {
  return <GreatGrandChild />;
}

function GreatGrandChild() {
  // Provider(<MyContext.Provider value={value}>)から
  // Context オブジェクトの値(value プロパティの値)を取得する。
  // そのため、context は
  // {
  //   name: 'soarflat',
  //   handleClick: () => setCount(count => count + 1)
  // }
  // になる。
  const context = useContext(MyContext);

  return (
    <>
      <p>{context.name}</p>
      <button onClick={context.handleClick}>increment</button>
    </>
  );
}

Contextを使うデメリット

下記が考えられるので無闇に使わず、使い所を見極めることがポイント。
①コンポーネントがContextに依存するため、コンポーネントの再利用性が低下する
②Contextオブジェクトの値はグローバルなstateのため、下手に使うとどこで利用されているかがわかりにくくなる

カスタムフック

自作のフックのことを指す。
具体的にいうと、コンポーネントから切り出したロジックを定義した関数です。

カスタムフックのメリット

①コンポーネント内のロジックを切り出すことで複数のコンポーネントで再利用できる
②コンポーネントから複雑なロジックを切り出すとコンポーネントの見通しが良くなる

使用例

import React, { useState } from "react";

// カスタムフック
// カスタムフックの名前はuseから始まる必要があります。
function useCounter(initialCount) {
  const [count, setCount] = useState(initialCount);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  return { count, increment, decrement };
}

// 再利用①
function Counter1() {
  const { count, increment } = useCounter(0);

  return (
    <>
      <p>count: {count}</p>
      <button onClick={increment}>+</button>
    </>
  );
}

// 再利用②
function Counter2() {
  const { count, decrement } = useCounter(10);

  return (
    <>
      <p>count: {count}</p>
      <button onClick={decrement}>-</button>
    </>
  );
}

function App() {
  return (
    <div>
      <Counter1 />
      <Counter2 />
    </div>
  );
}

export default App;

続きの記事

ざっくりReact Hooks入門①
ざっくりReact Hooks入門②

1
2
1

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?