リアクトフック
- ステータスusestate
- サイドエフェクト useEffect
- コンテキスト useContext
- Redux useReducer
- メモuseMemo
- 参考文献 useRef
- カスタム・フック
useState
使用状況
const [n,setN] = React.useState(0)
const [user,setUser] = React.useState({name:''})
ローカル更新なし
stateがオブジェクトの場合、部分的にsetStateすることはできません。
ボタンをクリックすると
年齢が表示されなくなります。
なぜなら、setStateはプロパティのマージに役立たないからです。
このメソッドを使うことができます。
変更するアドレス
setState(obj), objのアドレスが変更されていない場合、Reactはデータが変更されていないとみなします。
useState 使用可能な関数
初期値が複雑な場合に使用できます。
const [state,setState] = useState(()=>{
 return initialState
})
この関数は初期状態を返し、一度だけ実行されます。
setState 受け付ける関数
setN(i=>i+1)
この方法はどのようなときに使うのですか?
import React, {useState} from "react";
import ReactDOM from "react-dom";
function App() {
 const [n, setN] = useState(0)
 const onClick = ()=>{
 setN(n+1) // setN(n+1) はnを変更しない
 setN(n+1) // nに2を加えることができないことに気づくだろう。
 // setN(i=>i+1)
 // setN(i=>i+1) // このようにして、2つの
 }
 return (
 <div className="App">
 <h1>n: {n}</h1>
 
 <button onClick={onClick}>+2</button>
 </div>
 );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
実は、この形式を使うべきなのです。
useReducer
useStateの代替です。これは => newState という形式のリデューサを受け取り、現在の状態とそれに付随するディスパッチメソッドを返します。
import React, { useState, useReducer } from "react";
import ReactDOM from "react-dom";
const initial = {
 n: 0
};
const reducer = (state, action) => {
 if (action.type === "add") {
 return { n: state.n + action.number };
 } else if (action.type === "multi") {
 return { n: state.n * 2 };
 } else {
 throw new Error("unknown type");
 }
};
function App() {
 const [state, dispatch] = useReducer(reducer, initial);
 const { n } = state;
 const onClick = () => {
 dispatch({ type: "add", number: 1 });
 };
 const onClick2 = () => {
 dispatch({ type: "add", number: 2 });
 };
 return (
 <div className="App">
 <h1>n: {n}</h1>
 <button onClick={onClick}>+1</button>
 <button onClick={onClick2}>+2</button>
 </div>
 );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
簡単に言うと、useReducerはuseStateのより複雑なバージョンです。
useContext
- 使用C = createContext(initial) コンテキストの作成
- <C.provider> スコープ
- スコープ内でコンテキストを使用するには useContext(C) を使用します。
import React, { createContext, useState, useContext } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const C = createContext(null);
function App() {
 console.log("App 実行された");
 const [n, setN] = useState(0);
 return (
 <C.Provider value={{ n, setN }}>
 <div className="App">
 <Baba />
 </div>
 </C.Provider>
 );
}
function Baba() {
 const { n, setN } = useContext(C);
 return (
 <div>
 パパn: {n} <Child />
 </div>
 );
}
function Child() {
 const { n, setN } = useContext(C);
 const onClick = () => {
 setN(i => i + 1);
 };
 return (
 <div>
 息子: {n}
 <button onClick={onClick}>+1</button>
 </div>
 );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useEffect
副作用
- 環境に対する変更は、document.title を変更するような副作用です。
- useEffect に副作用を記述する必要はありません。
- 実際には、各レンダーの後に実行されるafterRenderと呼ぶ方が良いかもしれません。
- componentDidMountとして使用し、第2引数に[]を指定します。
- componentDidUpdateとして使用し、依存関係を指定できます。
- componentWillUnmountとして使用されます。
- これら3つの使用法は、同時に存在することができます。
同時に複数の useEffect が存在する場合、それらは出現した順に実行されます。
useLayoutEffect
useEffect は、ブラウザのレンダリング終了後に実行されます。
useLayoutEffect はブラウザがレンダリングされる前に実行されます。
- useLayoutEffect は常にuseEffectの前に実行されます。
- useLayoutEffのタスクは、理想的にはレイアウトに影響を与えます。
ユーザーエクスペリエンスのためには、useEffectが望ましいです。
useMemo
リアクトメモ
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
 const [n, setN] = React.useState(0);
 const [m, setM] = React.useState(0);
 const onClick = () => {
 setN(n + 1);
 };
 return (
 <div className="App">
 <div>
 <button onClick={onClick}>update n {n}</button> 
 </div>
 <Child data={m}/>
 {/* <Child2 data={m}/> */}
 </div>
 );
}
function Child(props) {
 console.log("child 実行された");
 console.log('ここに多くのコードがあるとして')
 return <div>child: {props.data}</div>;
}
const Child2 = React.memo(Child);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Reactはデフォルトではレンダリングが冗長で、ボタンをクリックするとChild()が実行されますが、Childの依存データは変更されないので、このときChildの代わりにReact.memo(Child)を使うことができます。
propsが変更されなければ、別の関数コンポーネントを実行する必要はありません。
ただし、React.memoにはバグがあります。
const onClickChild = ()=>{}
このコンポーネントにリスナー関数を渡すと、リスナー関数が何もしなくても、外部コンポーネントのデータが変更されて再レンダリングされるたびにコンポーネントが実行されます。
これは、App()が再実行されるたびに、コンポーネント内のものとは異なるアドレスで新しいリスナー関数が生成されるため、このコンポーネントも実行されてしまうためです。
この問題を解決するには、useMemo を使用します。
const onClickChild = useMemo(()=>{ return console.log(m) },[m])
useMemoの特徴
- 最初のパラメータは()=> value
- 2番目の引数は依存関係[m,n]です。
- 依存関係が変更された場合のみ新しい値が計算され、依存関係が変わらない場合は値が再利用されます。
値が関数の場合は、useMemo(=> console.log(x))と記述します。
これは関数を返す関数であり、使いにくいのでuseCallbackとします。
useCallback
useCallback(x=>log(x),[m]) useMemoに相当する=>x=>log(x),[m])
useRef
- コンポーネントが何度もレンダリングされるときに同じ値が必要な場合
- 初期化: const count=useRef(0)
- 読み方:カウント.current
- なぜ current が必要かというと、useRef が 2 回同じ値であることを確認するためです。
forwardRef
プロパティを渡すことはできません。
import React, { useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
 const buttonRef = useRef(null);
 return (
 <div className="App">
 <Button3 ref={buttonRef}> </Button3>
 </div>
 );
}
const Button3 = React.forwardRef((props, ref) => {
 return <button className="red" ref={ref} {...props} />;
});
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useImperativeHandle
をカスタマイズするために使用されるプロパティです。
const Button2 = React.forwardRef((props, ref) => {
 const realButton = createRef(null);
 const setRef = useImperativeHandle;
 setRef(ref, () => {
 return {
 x: () => {
 realButton.current.remove();
 },
 realButton: realButton
 };
 });
 return <button ref={realButton} {...props} />;
});
カスタム・フック
データ操作のカプセル化
フック/useList.js
import { useState, useEffect } from "react";
const useList = () => {
 const [list, setList] = useState(null);
 useEffect(() => {
 ajax("/list").then(list => {
 setList(list);
 });
 }, []); // [] を実行するのは
 return {
 list: list,
 setList: setList
 };
};
export default useList;
function ajax() {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
 resolve([
 { id: 1, name: "Frank" },
 { id: 2, name: "Jack" },
 { id: 3, name: "Alice" },
 { id: 4, name: "Bob" }
 ]);
 }, 2000);
 });
}
インデックス.js
import React, { useRef, useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useList from "./hooks/useList";
function App() {
 const { list, setList } = useList();
 return (
 <div className="App">
 <h1>List</h1>
 {list ? (
 <ol>
 {list.map(item => (
 <li key={item.id}>{item.name}</li>
 ))}
 </ol>
 ) : (
 " ..."
 )}
 </div>
 );
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);





