見出し画像

Reactフック まとめ

「Reactフック」についてまとめました。サンプルはReact Nativeで記述してます。

・react@18.3.1


1. Reacteフック

Reactフック」は、Reactの関数コンポーネントで状態管理やライフサイクルを利用するためのAPIです。フックはReact 16.8で導入され、クラスコンポーネントの代わりに関数コンポーネントを使用することを可能にし、コードをより簡潔でスマートに記述できるようになりました。

・Stateフック
・Contextフック
・Refフック
・Effectフック
・Performanceフック

2. Stateフック

状態をコンポーネントに記憶させることができます。状態の初期値を引数に取り、状態の現在の値とその値を更新する関数を返します。

・useState : 直接更新できる状態変数を宣言
・useReducer : reducer関数内で更新ロジックを使用して状態変数を宣言

2-1. useState

import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// アプリ
const App: React.FC = () => {
  // useStateフックで状態管理
  const [count, setCount] = useState<number>(0);

  // ボタンクリック時に呼ばれる
  const incrementCount = () => {
    setCount(count + 1);
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Count: {count}</Text>
      <Button title="Increment" onPress={incrementCount} />
    </View>
  );
};

export default App;

2-2. useReducer

import React, { useReducer } from 'react';
import { View, Text, Button } from 'react-native';

// StateとActionの型の定義
type State = number;
type Action = { type: 'increment' };

// reducer関数の定義
const countReducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return state + 1;
    default:
      return state;
  }
};

// アプリ
const App: React.FC = () => {
  // useReducerフックで状態管理
  const [count, dispatch] = useReducer(countReducer, 0);

  // ボタンクリック時に呼ばれる
  const incrementCount = () => {
    dispatch({ type: 'increment' });
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Count: {count}</Text>
      <Button title="Increment" onPress={incrementCount} />
    </View>
  );
};

export default App;

3. Contextフック

コンポーネントは情報をプロパティとして渡すことなく、遠くの親から情報を受け取ることができます。

・useContext : コンテキストを読み取り、サブスクライブ

3-1. useContext

import React, { createContext, useContext, useState, ReactNode, FC } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

// テーマの種類の定義
type Theme = 'light' | 'dark';

// コンテキストの型の定義
interface ThemeContextType {
  theme: Theme;
}

// コンテキストの作成
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

// テーマプロバイダーコンポーネント
const ThemeProvider: FC<{ children: ReactNode }> = ({ children }) => {
  // useStateフックで状態管理
  const [theme, setTheme] = useState<Theme>('light');

  // ボタン押下時に呼ばれる
  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme }}>
      <View style={{ flex: 1 }}>
        {children}
        <View>
          <Button title="Toggle Theme" onPress={toggleTheme} />
        </View>
      </View>
    </ThemeContext.Provider>
  );
};

// テーマレシーバーコンポーネント
const ThemedComponent: FC = () => {
  const { theme } = useContext(ThemeContext)!;

  return (
    <View style={[theme === 'light' ? styles.light : styles.dark]}>
      <Text style={[theme === 'light' ? styles.darkText : styles.lightText]}>
        The current theme is {theme}
      </Text>
    </View>
  );
};

// アプリ
const App: FC = () => {
  return (
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );
};

// スタイル
const styles = StyleSheet.create({
  light: {
    backgroundColor: '#fff',
  },
  dark: {
    backgroundColor: '#333',
  },
  lightText: {
    color: '#fff',
  },
  darkText: {
    color: '#000',
  },
});

export default App;

4. Refフック

DOMノードやタイムアウトID など、レンダーに用いない情報を保持することができます。stateと違い、refの値を更新してもコンポーネントは再レンダーされません。

・useRef : refを宣言。多くの場合、DOMノードを格納
・useImperativeHandle : コンポーネントが公開するrefをカスタマイズできる。ほとんど用いられることはない

4-1. useRef


import React, { useRef } from 'react';
import { View, TextInput, Button } from 'react-native';

// アプリ
const App: React.FC = () => {
  // useRefでDOMノードを保持
  const inputRef = useRef<TextInput>(null);

  // ボタン押下時に呼ばれる
  const focusTextInput = () => {
    // 参照でDOMノードを操作
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  return (
    <View>
      <TextInput
        ref={inputRef}
        placeholder="Enter text here"
      />
      <Button title="Focus Text Input" onPress={focusTextInput} />
    </View>
  );
};

export default App;

5. Effectフック

コンポーネントを外部システムに接続して同期できます。これには、通信、ブラウザDOM、アニメーション、別UIライブラリで記述されたウィジェット、その他のReact以外のコードの処理が含まれます。

useEffectの他に、発火タイミングが異なるuseLayoutEffectとuseInsertionEffectも提供されています。

・useEffect : 外部システムとコンポーネントを接続。
・useLayoutEffect : ブラウザが画面を再レンダーする前に発火。レイアウトを測定できる
・useInsertionEffect : DOM変更前に発火。動的なCSSを挿入できる

useEffectのパラメータは、次のとおりです。

・第1引数 : 実行する関数
・第2引数 : 依存する値の配列。依存する値が変更されたときに関数実行。空配列の場合はコンポーネントのマウント時に関数実行
・戻り値 : クリーンアップ関数。Effectの再実行前、コンポーネントのアンマウント前に実行

5-1. useEffect

import React, { useState, useEffect } from 'react';
import { View, Text, Button } from 'react-native';

// アプリ
const App: React.FC = () => {
  // useStateフックで状態管理
  const [count, setCount] = useState<number>(0);

  // useEffectフックで副作用実行
  useEffect(() => {
    console.log(`Count changed: ${count}`);

    return () => {
      console.log(`Cleanup: Count was ${count}`);
    };
  }, [count]);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Count: {count}</Text>
      <Button title="Increment" onPress={() => setCount(count + 1)} />
    </View>
  );
};

export default App;

6. Performanceフック

再レンダーのパフォーマンスを最適化するためのに、不要な処理を減らすためのフックです。

・useMemo  : 高負荷な計算結果をキャッシュ
・useCallback : 最適化済みのコンポーネントに渡すために関数定義をキャッシュ

6-1. useMemo

import React, { useMemo } from 'react';
import { View, Text } from 'react-native';

// アプリ
const App: React.FC = () => {
  const numbers = [1, 2, 3, 4, 5];

  // useMemoフックで計算結果キャッシュ (numbers変更されるまでキャッシュ使用)
  const sum = useMemo(() => {
    console.log('Calculating sum...');
    return numbers.reduce((acc, num) => acc + num, 0);
  }, [numbers]);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Sum of numbers: {sum}</Text>
    </View>
  );
};

export default App;

6-2. useCallback

import React, { useState, useCallback } from 'react';
import { View, Button, Text } from 'react-native';

// アプリ
const App: React.FC = () => {
  // useStateフックで状態記憶
  const [count, setCount] = useState<number>(0);

  // useCallbackフックで関数キャッシュ (再レンダーでの関数再作成を防止)
  const increment = useCallback(() => {
    setCount(prevCount => prevCount + 1);
  }, []);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>{count}</Text>
      <Button title="Increment" onPress={increment} />
    </View>
  );
};

export default App;



いいなと思ったら応援しよう!