[React] Hooks
Hooks
HookはReact 16.8バージョンから新しく追加されたステータスとライフサイクル管理APIです.Hookでは、クラスエレメントを使用せずに関数エレメント内にステータスを保存し、ライフサイクル管理を行うことができます.
useState
useStateはステータス管理用のhookです.
Create initial state
const [<상태 저장 변수>, <상태 갱신 함수>] = useState(<상태 초기 값>);
初期状態生成時には、状態格納変数の初期値をarray、number、booleanなど複数のタイプと値に指定できます.また、userStateは、パラメータとして非値関数を使用できます.この場合、最初のレンダリング時にのみ実行されます.
Update State
const [count, setCount] = useState(0)
setCount(prevCount => prevCount + 1)
初期状態生成時には、宣言された状態更新関数で状態格納変数の値を更新できます.
Example
useState hookとステータスストレージ変数countを使用したカウンタの例function Counter() {
const [count, setCount] = useState(0) // creating initial state
function changeCount(amount) {
setCount(prevCount => prevCount + amount)
}
function resetCount() {
setCount(0)
}
return (
<>
<span>{count}</span>
<button onClick={() => changeCount(1)}>+</button>
<button onClick={() => changeCount(-1)}>-</button>
<button onClick={() => resetCount()}>Reset</button>
</>
)
}
useEffect
useEffect(function, deps)
userEffectは、クラス要素のライフサイクルメソッドに代わって、要素がレンダリング時またはレンダリング後に特定の操作を実行できるようにするhookです.
Creating Your First Side Effect
ネットワークリクエスト、データインポート、サブスクリプション設定、構成部品の手動変更に影響するDOMなど、現在の関数を超える範囲を副作用と呼びます.useEffectは、これらの副作用を処理するために使用される.
userEffectを使用する最も基本的な方法は、単一の関数(single function)を渡すことです.useEffect(() => {
console.log('This is a side effect')
})
この場合、この操作は、エレメントの最初のマウント、propsの変更、ステータスの変更など、レンダリングのたびに実行されます.ただし、取り付け段階でのみ副作用が必要な場合や、一部の支柱や状態が変更される場合は望ましくありません.
2番目のパラメータである依存配列(Dependency Array)を使用すると、最後のレンダーの依存性が前のレンダーの依存性と比較して変化すると、副作用が発生します.インストールフェーズでのみ実行する場合は、空の配列を2番目のパラメータとして渡すことができます.useEffect(() => {
console.log('Only run on mount')
}, [])
useEffect(() => {
console.log('Only run on url change')
}, [url])
Cleaning Up Side Effects
アンインストール前、更新前に副作用が必要な場合はcleanup関数を返さなければなりません.useEffect(() => {
console.log('This is my side effect')
return () => {
console.log('This is my clean up')
}
})
インストール後、上記のuseEffectを含む構成部品を2回再レンダリングしてアンインストールすると、コンソール出力は次のようになります.// MOUNTED
// This is my side effect
// RE-RENDER 1:
// This is my clean up
// This is my side effect
// RE-RENDER 2:
// This is my clean up
// This is my side effect
// UN-MOUNT:
// This is my clean up
useMemo & useCallback
useMemoとuseCallback hookを理解するにはmemoizationを理解する必要があります.
What is Memoization?
Memoizationは本質的にキャッシュと同じ意味です.同じ計算を繰り返す関数を実行する場合、値を再計算するよりも、キャッシュの値を使用して処理速度を迅速に向上させます.const cache = {}
function slow(a) {
if (cache[a]) return cache[a]
const result = /* Complex logic */
cache[a] = result
return result
}
すべてのエレメントロジックがレンダリングされるたびに再計算され、ロジックの計算速度が遅い場合に急激に低下する問題を解決するために、hookはuseMemoとuseCallbackです.
useMemo
useMemoはuseEffectと同様に動作し、2番目のパラメータ依存性が変化した場合にのみmemorization値を再計算します.すなわち、useMemoはコメントされた値を返します.const result = useMemo(() => {
return slowFunction(a)
}, [a])
userMemoに渡される関数は、レンダリング中に実行されます.2番目のパラメータがない場合は、レンダーするたびに新しい値が計算されます.
useCallback
useCallbackは依存配列キャッシュ結果に基づいているため、useMemoと同様に動作します.ただし、useCallbackはキャッシュの値ではなく、キャッシュの関数に使用されます.すなわち、useCallbackは、コメントされたコールバックを返します.const handleReset = useCallback(() => {
return doSomething(a, b)
}, [a, b])
構文はusemoと同じですが、依存性が変化するたびに、usemoは伝達された関数を呼び出し、呼び出しの値を返します.逆に、依存性が変化するたびにuseCallbackは関数を呼び出すのではなく、関数の新しいバージョンを返します.useCallback(() => {
return a + b
}, [a, b])
useMemo(() => {
return () => a + b
}, [a, b])
上のコードでは、useCallbackとuseMemoは同じ値を返しますが、useCallbackは伝達された関数を返し、useMemoは伝達された関数の結果を返します.
また、useMemoとuseCallbackは、参照を等しく保つために使用されます.function Parent() {
const [items, setItems] = useState([])
const handleLoad = (res) => setItems(res)
return <Child onLoad={handleLoad} />
}
function Child({ onLoad }) {
useEffect(() => {
callApi(onLoad)
}, [onLoad])
}
上記の例では、handleLoad
要素がレンダリングされるたびに、Parent
関数が再作成されます.これは、onLoad
関数が各レンダリングにおいて異なる参照同等性を有するため、Child
要素の使用効果が再実行されることを意味する.function Parent() {
const [items, setItems] = useState([])
const handleLoad = useCallback((res) => setItems(res), [])
return <Child onLoad={handleLoad} />
}
function Child({ onLoad }) {
useEffect(() => {
callApi(onLoad)
}, [onLoad])
}
handleLoad
でuseCallbackを使用すると、handleLoad
関数は変更されず、Child
要素がレンダリングされるたびに呼び出されません.
useRef
userefは、DOM要素に直接アクセスおよび操作するためのhookです.また、レンダリング間でデータを保持することもできます.
userefを使用するには、まずrefを初期化する必要があります.useRef(initialValue)
userefはcurrentという単一のPropertyというオブジェクトを返し、初期値は現在のPropertyに割り当てられます.const myRef = useRef(0);
console.log(myRef);
// { current: 0 }
宣言後にuserefを再レンダリングしても、同じ参照は続行されます.また、参照が変更されても、構成部品は再レンダリングされません.すなわち、refはレンダリング間の持続値を格納するオブジェクトである.function State() {
const [rerenderCount, setRerenderCount] = useState(0);
useEffect(() => {
setRerenderCount(prevCount => prevCount + 1);
});
return <div>{rerenderCount}</div>;
}
function Ref() {
const rerenderCount = useRef(0);
useEffect(() => {
rerenderCount.current = rerenderCount.current + 1;
});
return <div>{rerenderCount.current}</div>;
}
State
構成部品では、ステータスが更新されると、構成部品は再レンダリングされますが、Ref
構成部品のrefは値が変更されたときに再レンダリングされません.
How to use Refs
refは、通常、DOM要素を参照するために使用される.たとえば、ボタンをクリックするたびにinput
要素にカーソルを移動する場合は、refを使用して次のコードを記述します.function Component() {
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</>
)
}
Using Refs beyond the DOM
refは、レンダリング間の記憶領域として使用することができる.unction Component() {
const [name, setName] = useState('Kyle')
const previousName = useRef(null)
useEffect(() => {
previousName.current = name
}, [name])
return (
<>
<input value={name} onChange={e => setName(e.target.value)} />
<div>{previousName.current} => {name}</div>
</>
)
}
上記の例は、ステータス変数name
が変更されるたびにrefが更新され、ステータス変数name
の以前の値を保存するコードである.
useContext
What is Context API?
反応器ではstateはデータとpropsを格納し,素子間でデータを伝達するために用いられる.しかしながら、多くの素子がネスト構造で状態を伝達するには、複数回の複雑なステップが必要であるため、メンテナンスが困難である.
Context APIを使用すると、コンポーネントを介してデータを渡すことなく、Context内のすべてのネストされたコンポーネントの使用可能なデータを指定できます.このデータは半グローバル状態であり,Context内部のどこでも利用できる.const ThemeContext = React.createContext()
function App() {
const [theme, setTheme] = useState('dark')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<ChildComponent />
</ThemeContext.Provider>
)
}
function ChildComponent() {
return <GrandChildComponent />
}
class GrandChildComponent {
render() {
return (
<ThemeContext.Consumer>
{({ theme, setTheme }) => {
return (
<>
<div>The theme is {theme}</div>
<button onClick={() => setTheme('light')}>
Change To Light Theme
</button>
</>
)
}}
</ThemeContext.Consumer>
)
}
}
上記のコードでは、React.createContext
を用いて2つの部分の変数が生成される.
第1の部分は、すべてのオーバーラップ要素に値を提供するプロバイダである.上記のコードは、theme
およびsetTheme
の単一のオブジェクトです.
第2部は消費者です.Contextの値にconsumerからアクセスするには、コードを記述する必要があります.構成部品のサブ要素をカプセル化する関数のパラメータとしてContext値を指定します.
しかし、第2部では、Contextの値を取得するために、関数を含むコンポーネントにJSXをカプセル化することで、コードの重複や複雑なレイヤが増加するという問題がある.
useContext
useContextを使用してContextをuseContext hookに渡し、consumerでJSXコードを記述することなく関数コンポーネントでContextを使用します.function GrandChildComponent() {
const { theme, setTheme } = useContext(ThemeContext)
return (
<>
<div>The theme is {theme}</div>
<button onClick={() => setTheme('light')}>
Change To Light Theme
</button>
</>
)
}
useContextを使用すると、既存のconsumerセクションの複雑な重複を解消できます.Contextは、Contextの通常の関数を呼び出すように、Context内部の値を提供します.useContextを設定するproviderは、既存のContext APIと全く同じです.
Reference
const [<상태 저장 변수>, <상태 갱신 함수>] = useState(<상태 초기 값>);
const [count, setCount] = useState(0)
setCount(prevCount => prevCount + 1)
function Counter() {
const [count, setCount] = useState(0) // creating initial state
function changeCount(amount) {
setCount(prevCount => prevCount + amount)
}
function resetCount() {
setCount(0)
}
return (
<>
<span>{count}</span>
<button onClick={() => changeCount(1)}>+</button>
<button onClick={() => changeCount(-1)}>-</button>
<button onClick={() => resetCount()}>Reset</button>
</>
)
}
useEffect(function, deps)
useEffect(() => {
console.log('This is a side effect')
})
useEffect(() => {
console.log('Only run on mount')
}, [])
useEffect(() => {
console.log('Only run on url change')
}, [url])
useEffect(() => {
console.log('This is my side effect')
return () => {
console.log('This is my clean up')
}
})
// MOUNTED
// This is my side effect
// RE-RENDER 1:
// This is my clean up
// This is my side effect
// RE-RENDER 2:
// This is my clean up
// This is my side effect
// UN-MOUNT:
// This is my clean up
const cache = {}
function slow(a) {
if (cache[a]) return cache[a]
const result = /* Complex logic */
cache[a] = result
return result
}
const result = useMemo(() => {
return slowFunction(a)
}, [a])
const handleReset = useCallback(() => {
return doSomething(a, b)
}, [a, b])
useCallback(() => {
return a + b
}, [a, b])
useMemo(() => {
return () => a + b
}, [a, b])
function Parent() {
const [items, setItems] = useState([])
const handleLoad = (res) => setItems(res)
return <Child onLoad={handleLoad} />
}
function Child({ onLoad }) {
useEffect(() => {
callApi(onLoad)
}, [onLoad])
}
function Parent() {
const [items, setItems] = useState([])
const handleLoad = useCallback((res) => setItems(res), [])
return <Child onLoad={handleLoad} />
}
function Child({ onLoad }) {
useEffect(() => {
callApi(onLoad)
}, [onLoad])
}
useRef(initialValue)
const myRef = useRef(0);
console.log(myRef);
// { current: 0 }
function State() {
const [rerenderCount, setRerenderCount] = useState(0);
useEffect(() => {
setRerenderCount(prevCount => prevCount + 1);
});
return <div>{rerenderCount}</div>;
}
function Ref() {
const rerenderCount = useRef(0);
useEffect(() => {
rerenderCount.current = rerenderCount.current + 1;
});
return <div>{rerenderCount.current}</div>;
}
function Component() {
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus()
}
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</>
)
}
unction Component() {
const [name, setName] = useState('Kyle')
const previousName = useRef(null)
useEffect(() => {
previousName.current = name
}, [name])
return (
<>
<input value={name} onChange={e => setName(e.target.value)} />
<div>{previousName.current} => {name}</div>
</>
)
}
const ThemeContext = React.createContext()
function App() {
const [theme, setTheme] = useState('dark')
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<ChildComponent />
</ThemeContext.Provider>
)
}
function ChildComponent() {
return <GrandChildComponent />
}
class GrandChildComponent {
render() {
return (
<ThemeContext.Consumer>
{({ theme, setTheme }) => {
return (
<>
<div>The theme is {theme}</div>
<button onClick={() => setTheme('light')}>
Change To Light Theme
</button>
</>
)
}}
</ThemeContext.Consumer>
)
}
}
function GrandChildComponent() {
const { theme, setTheme } = useContext(ThemeContext)
return (
<>
<div>The theme is {theme}</div>
<button onClick={() => setTheme('light')}>
Change To Light Theme
</button>
</>
)
}
Reference
この問題について([React] Hooks), 我々は、より多くの情報をここで見つけました https://velog.io/@iwaskorean/React-Hooksテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol