Open hysryt opened 6 years ago
関数コンポーネントに state 機能を追加する。 ステートとはコンポーネントの状態(コンポーネントが持つデータ)のことを言う。 ステートは各コンポーネントごとに保持されるため、同じコンポーネントであっても値が共有されることはない。
useState
関数を呼び出すことでデータを表す変数とデータを更新する関数を得ることができる。
const [count, setCount] = useState(0);
count
に現在のステートの値、setCount
にステートの値を更新する関数が格納される。
useState
は常に同じ順番で実行されなければならない。
つまり、if文の中に設置して特定の条件下でのみ実行する、ということはできない。
if文の中に設置しようとするとコンパイルエラーとなる。
レンダー後に処理を追加する。
useEffect
関数を呼び出すことで処理を追加できる。
useEffect(() => {
document.title = `You clicked ${count} times`;
});
useEffect
で追加した処理は画面更新をブロックしない。
画像更新ブロックさせる(画像更新と同期的に行う必要がある)場合は useLayoutEffect
を使用する。
useEffect
にわたした関数が戻り値として関数を返す場合、Reactはその関数をコンポーネントのアンマウント時と、2回目以降の副作用実行時の前に実行する。
第二引数には依存する変数を配列で指定できる。 依存する変数を指定することで、その変数が変更された時のみ処理を実行させることができる。
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // count が変更された時のみ document.title を変更する
レンダーされたDOM要素あるいはReact要素への参照を取得する。
要素に対して関数を実行したい場合(canvas要素の描画など)などは props
の受け渡しだけでは実現できない。
そのような場合はrefと要素のref属性を使ってその要素への参照を取得し、そのrefを使って関数を実行する。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.canvasRef = React.createRef();
}
componentDidUpdate(prevProps){
const ctx = this.canvasRef.current.getContext("2d");
// ...
}
render() {
return <canvas ref={this.canvasRef} />;
}
}
関数コンポーネントに対して ref 属性を使用することはできない。
refと同じ働きをする。 それに加え、useRefは毎回のレンダーで同じオブジェクトを返すため、クラスのインスタンス変数のように使用することもできる。 監視されないステートとも言える。
npx create-react-app my-app
TypeScriptを使用したい場合は、
npx create-react-app my-app --template typescript
webpackやbabelなどの設定が既に行われたアプリケーションを生成できる。
コンポーネントをメモ化する。
関数をメモ化する。
動的importしたコードはコード分割の対象となる。
React.lazy
と一緒に使うことが多い。
コンポーネントの遅延読み込みを行う。
const AddButton = lazy(() => import('./AddButton'));
引数でPromiseを返す関数をとる。
そのPromiseはコンポーネントを解決する必要がある。
import
は動的にコンポーネントを読み込む関数なのでそれに該当する。
React.lazy
は関数を返す。
その関数はコンポーネントとして使用できる。
仕組み的には、その関数が実行されるとまずコンポーネントが既に読み込まれているかどうかを確認する。
読み込まれていればそれをそのまま返し、
読み込まれていなければReact.lazyに渡した関数を実行して読み込む。
このとき、そのPromiseをthrowする。
throwすることでSuspenseでキャッチでき、ローディング画面を表示できる。
React.lazy
で import したコンポーネントは <Suspense>
コンポーネントで囲う必要がある。
<Suspense fallback={<div>loading...</div>}>
<AddButton disabled={item.trim() === ''} onClick={handleAdd} />
</Suspense>
import ReactDOM from "react-dom";
ReactDOM.render(
<div>Hello, World!</div>,
document.getElementById('root');
);
この中の
<div>Hello, World!</div>
はJSXと呼ばれるシンタックスシュガー。 Babelによって、React要素を生成するJavaScriptへと変換される。
render
関数は第二引数の配下に第一引数のReact要素をDOM要素としてレンダリングする。
Reactアプリはコンポーネント単位で開発される。 コンポーネントは親子関係を持つことができる。
親コンポーネントから子コンポーネントへデータを受けわたす方法の一つとして、props
がある。
HTMLでいう属性。
<MyComponent name="john" />
上記の例では、MyComponentというコンポーネントに対して、propsでキーがname、値がjohnというデータを渡している。
子コンポーネント側では this.props.name
で受け取った値(john)を取得できる。
Reactに限らずJavaScript全般で言えるが、モジュールは複数回importしたとしても実行されるのは一度のみ。 別々のファイルから同じモジュールをimportしたとしても実行されるのは一度のみ。 動的importの場合も同様。
親コンポーネントから受け渡すことなくデータを参照できる。 データを更新したい場合は更新用の関数を親コンポーネントから受け取る必要がある。
const MyContext = React.createContext(defaultValue);
<MyContext.Provider value={/* 何らかの値 */}>
...
</MyContext.Provider>
const theme = useContext(ThemeContext);
Reactを使ってサイトを作る場合は以下の方法がある。
Next.jsの方が機能が豊富に見える。
ステートの管理を楽にする為のライブラリがいくつかある。
一般的には併用はせず、どれか一つを使う。
useCallback(fn, deps)
依存関係が同じ場合は常に同じコールバック関数を返す。
通常、コールバック関数は常に新規で生成される。
const callback = () => {
console.log('callback!');
};
これをpropsとしてコンポーネントに渡すと、計算は同じでも別のコールバック関数と解釈される。 その結果、無駄な再レンダリングが発生する。
そこで、useCallback
フックは依存関係が同じであれば同じコールバック関数を返す、という機能を提供する。
これを props に渡せば計算が同じであれば同じコールバック関数として解釈されるため、無駄な再レンダリングがなくなる。
const callback = useCallback(() => {
console.log('callback');
}, []);
上記の場合は依存関係を何も指定していないため、常に同じコールバック関数を返す。
参考 https://qiita.com/uehaj/items/99f7cd014e2c0fa1fc4e https://blog.uhy.ooo/entry/2021-02-23/usecallback-custom-hooks/
const [state, dispatch] = useReducer(reducer, initialArg, init);
Reduxでお馴染みのreducer。 引数のreducerにはアクションとステートをもとに新しいステートを返す関数を渡す。
reducerとstateが1対1になるという認識で良さそう。
https://reactjs.org/