Udemyのこちらのコースを参考にしています。「React Hooks 入門 - Hooksと Redux を組み合わせて最新のフロントエンド状態管理手法を習得しよう!」
useState
classコンポーネントのthis.state
、setState
と同じ機能
const [{state名}, {stateを変更するときの関数名} = useState({初期値})]
// 基本的な書き方
const [count, setCount] = useState(0);
// 初期値はobjectでも書ける
const [statem, setState] = useState({
name: 'ichiro',
age: 51,
});
const { name, age } = state;
import React, { useState } from 'react';
const Sample = () => {
const [count, setCount] = useState(0);
return (
<>
<p>count: {count}</p>
<button type="button" onClick={() => setCount(count + 1)}>Click</button>
</>
);
};
export default Sample;
useEffect
classコンポーネントのライフサイクルメソッドと同じ機能
// componentDidMountとcomponentDidUpdateと同じ挙動
useEffect(() => {
// 処理を書く
});
// componentDidMountと同じ挙動
useEffect(() => {
// 処理を書く
}, []);
// hogeの値のみcomponentDidMountとcomponentDidUpdateと同じ挙動
useEffect(() => {
// 処理を書く
}, [hoge]);
import React, { useState, useEffect } from 'react';
const Sample = () => {
const [count, setCount] = useState(0);
useEffect(() => {
alert('stateのcountが更新されたよ');
}, [count]);
return (
<>
<p>count: {count}</p>
<button type="button" onClick={() => setCount(count + 1)}>Click</button>
</>
);
};
export default Sample;
useReducer
reducerにdispatchして更新されたstateを取得する
const [{現在のstate}, {dispatch関数}] = useReducer[{reduce関数} , {初期値}]
const [state, dispatch] = useReducer(reducer, []);
import React, { useReducer } from 'react';
const Sample = () => {
const reducer = (state = [], action) => {
switch(action.type) {
case 'CREATE':
const personal = { name: action.name, age: action.age }
return [...state, {...personal}];
default:
return state;
};
};
const create = () => {
dispatch({
type: 'CREATE',
name: 'ichiro',
age: 51,
});
};
const [state, dispatch] = useReducer(reducer, []);
return (
<>
<button type="button" onClick={create}>Click</button>
<ul>
{
state.map((s, index) => (
<li key={index}>
<p>名前は{s.name}</p>
<p>年齢は{s.age}</p>
</li>
))
}
</ul>
</>
);
};
export default Sample;
useContext
- reduxのconnect関数に変わるもの
- Providerでwrapされたcomponentは、props valueのデータをバケツリレーなしで利用できる
// ProviderとConsumer(useContext)を利用できるようにcreateContextを読み込む
import { createContext } from 'react';
const AppContext = createContext();
export default AppContext;
import React, { useReducer } from 'react';
import AppContext from './AppContext';
import Sample from './Sample'
const App = () => {
const reducer = (state = [], action) => {
switch(action.type) {
case 'CREATE':
const personal = { name: action.name, age: action.age }
return [...state, {...personal}];
default:
return state;
};
};
const [state, dispatch] = useReducer(reducer, []);
return (
<AppContext.Provider value={{ state, dispatch }}>
// Sample comonentにstateとdispatchがバケツリレーなしで利用可能に
<Sample />
</AppContext.Provider>
)
}
export default App;
// Providerのvalueのデータを受け取るために`useContext'を読み込む
import React, { useContext } from 'react';
import AppContext from './AppContext';
const Sample = () => {
const { state, dispatch } = useContext(AppContext);
const create = () => {
dispatch({
type: 'CREATE',
name: 'ichiro',
age: 51,
});
};
return (
<>
<button type="button" onClick={create}>Click</button>
<ul>
{
state.map((s, index) => (
<li key={index}>
<p>名前は{s.name}</p>
<p>年齢は{s.age}</p>
</li>
))
}
</ul>
</>
);
};
export default Sample;
useMmemo
以下、公式サイトからの引用です。
“作成用” 関数とそれが依存する値の配列を渡してください。useMemoは依存配列の要素 のひとつが変化した場合にのみメモ化された値を再計算します。この最適化によりレンダー毎に高価な計算が実行されるのを避けることができます。
useMemoに渡した関数はレンダー中に実行されるということを覚えておいてください。レンダー中に通常やらないようなことをこの関数内でやらないようにしましょう。例えば副作用は useMemo ではなく useEffectの仕事です。
以下「雰囲気で使わないReact hooksのuseCallback/useMemo」からの引用になります。
useMemoを使うとパフォーマンスが上がる、値を保持する処理は全てuseMemoを使うのかと思ったんですが、何かしらの複雑な計算のみに使用するようです。
const memoedValue = React.useMemo(() => /* 何かしらの複雑な計算 */, [])
複雑な計算ってどれくらい?
と思いますよね?
この処理を超えるときはuseMemoを使う
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y)
);
}
useCallback
- 子コンポーネントのpropsに処理を渡すときに使用
- 親コンポーネントのrenderの度に処理が発生するのを回避するために「deps」で指定した値に変化があったときだけ処理を行なう
- onClick属性で関数を書くとき、event時のみに処理を行わせる
.bind
と同じという認識
depsに指定する要素
どの要素を指定すればいいのか分からなくなるときがあるのでESLintを使う方法あり
以下「雰囲気で使わないReact hooksのuseCallback/useMemo」からの引用になります。
基本的なプラクティスとしては「useCallback/useMemoの中で参照する要素は全て depsに書く」です。とりあえずそうしておけば必ず最新の値には保たれます。
また、このリスクを軽減する方法としてeslintのexhaustive-depsがあります。
lazy/Suspense
- lazyは動的componentに適用
- Suspense(動的componentの待機中の処理)と併用
lazyについて公式サイトから引用
React.lazy 関数を使用すると、動的インポートを通常のコンポーネントとしてレンダリングすることができます。
※React.lazyとSuspenseはまだサーバーサイドレンダリングには使用できません。
Suspenseについて公式サイトから引用
遅延コンポーネントは、Suspense コンポーネント内でレンダーされる必要があります。これによって、遅延コンポーネントのローディングの待機中にフォールバック用のコンテンツ(ローディングインジケータなど)を表示できます。
import React, { lazy, Suspense } from 'react';
const Hoge = lazy(() => import('./Hoge'));
const Sample = () => (
<>
<Suspense fallback={<div>loading...</div>}>
<Hoge />
</Suspense>
</>
);