Reactフック まとめ
「Reactフック」についてまとめました。サンプルはReact Nativeで記述してます。
1. Reacteフック
「Reactフック」は、Reactの関数コンポーネントで状態管理やライフサイクルを利用するためのAPIです。フックはReact 16.8で導入され、クラスコンポーネントの代わりに関数コンポーネントを使用することを可能にし、コードをより簡潔でスマートに記述できるようになりました。
2. Stateフック
状態をコンポーネントに記憶させることができます。状態の初期値を引数に取り、状態の現在の値とその値を更新する関数を返します。
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フック
コンポーネントは情報をプロパティとして渡すことなく、遠くの親から情報を受け取ることができます。
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の値を更新してもコンポーネントは再レンダーされません。
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のパラメータは、次のとおりです。
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フック
再レンダーのパフォーマンスを最適化するためのに、不要な処理を減らすためのフックです。
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;