Edited at

🎉React 16.8: æ­Ŗåŧį‰ˆã¨ãĒãŖたReact HooksをäģŠã•ã‚‰įˇã–らいする

æĨたる 2 月 4 æ—Ĩ、ついãĢ React 16.8 ぎæ­Ŗåŧį‰ˆãŒãƒĒãƒĒãƒŧ゚されぞす。こぎ React 16.8 ãĢは、ã‚ĸãƒĢãƒ•ã‚Ąį‰ˆãŒå…Ŧ開されãĻäģĨæĨ常ãĢ React ãƒĻãƒŧã‚ļãƒŧãŸãĄãŽé–ĸåŋƒã‚’ãģしいぞぞãĢしãĻきたReact Hooksがæ­Ŗåŧį‰ˆã¨ãĒãŖãĻčŋŊ加されぞす。

â€ģčŋŊ記īŧšã‚ĸãƒĄãƒĒã‚Ģ時間で 2 月 5 æ—ĨãĢãĒãŖãĻも React 16.8 がãƒĒãƒĒãƒŧ゚されぞせんでした。äē‹å‰æƒ…å ąã§ 2 月 4 æ—Ĩã¨č¨€ãŖãĻたぎãĢâ€Ļâ€Ļ。いつãĢãĒãŖたらãƒĒãƒĒãƒŧ゚されるぎかはよく分かりぞせん。2 月 6 æ—ĨãĢ React 16.8 がãƒĒãƒĒãƒŧ゚されぞしたīŧ

į†ąåŋƒãĒ React ãƒĻãƒŧã‚ļãƒŧぎ斚は、åŊ“į„ļ React Hooks ãŽæƒ…å ąã‚’å¸¸ãĢčŋŊãŖãĻおりæ­Ŗåŧį‰ˆãŒãƒĒãƒĒãƒŧ゚されたらすぐãĢでもč‡Ē分ぎã‚ŗãƒŧドでäŊŋいはじめるæē–備ができãĻã„ã‚‹ã“ã¨ã¨æ€ã„ãžã™ã€‚ã—ã‹ã—ã€ã“ãŽč¨˜äē‹ã‚’ごčĻ§ãŽæ–šã€…ぎ中ãĢは React をäŊŋãŖãĻいるãĢもé–ĸわらず「React Hooks ぎことはよく分からãĒã„ã€ã¨ã‹ã€Œčžã„ãŸã“ã¨ãã‚‰ã„ã¯ã‚ã‚‹ã‘ãŠäģŠãŠã†ãĒãŖãĻいるぎかįŸĨらãĒい」とか「æ­Ŗåŧį‰ˆãŒå‡ēãĻからčĒŋずればいいやと思ãŖãĻã„ãŸã€ã¨ã„ã†æ–šãŒã‚‚ã—ã‹ã—ãŸã‚‰åą…ã‚‹ã‹ã‚‚ã—ã‚Œãžã›ã‚“ã€‚

いよいよæ­Ŗåŧį‰ˆãŽį™ģ場とį›¸æˆãŖãĻしぞいぞしたから、įš†ã•ã‚“はもう React Hooks から逃げることはできぞせん1ã€‚ã“ãŽč¨˜äē‹ã§ã¯ä¸Šč¨˜ãŽã‚ˆã†ãĒæ–šã€…ã‚’å¯žčąĄã¨ã—ãĻ、React Hooks ぎæ­Ŗåŧį‰ˆãĢ備えãĻおんãĒフックがあるぎかをすずãĻčĒŦ明しぞす。ã‚ĸãƒĢãƒ•ã‚Ąį‰ˆãŽã†ãĄã¯ API ぎ変化もありぞしたから、少し前ãĢãĄã‚‡ãŖとčĒŋずãĻãŋたけおäģŠãŠã†ãĒãŖãĻいるぎかは分からãĒいというようãĒäēēはčĻãƒã‚§ãƒƒã‚¯ã§ã™ã€‚ãĒお、React はドキãƒĨãƒĄãƒŗトぎ充原ãĢ力をå…ĨれãĻおり、React Hooks ãĢé–ĸしãĻもå…Ŧåŧãƒ‰ã‚­ãƒĨãƒĄãƒŗãƒˆãŒãĄã‚ƒã‚“ã¨æœ€æ–°ãŽįŠļ態ãĢäŋãŸã‚ŒãĻã„ãžã™ã€‚ã§ã™ã‹ã‚‰ã€č‹ąčĒžãŒčĒ­ã‚ã‚‹äēēã¯ã“ãŽč¨˜äē‹ã‚’čĒ­ãžãšãĢå…Ŧåŧãƒ‰ã‚­ãƒĨãƒĄãƒŗトをčĒ­ã‚€ãŽã‚‚よいかもしれぞせん。

記äē‹ä¸­ãĢį™ģ場するã‚ĩãƒŗプãƒĢã‚ŗãƒŧドはGitHubãĢįŊŽã„ãĻありぞす。


React Hooks ぎæĻ‚čĻ

įˇã–らいというã‚ŋイトãƒĢã‚’ã¤ã‘ãŸč¨˜äē‹ãĒぎで、一åŋœ React Hooks ãĢついãĻåŸēį¤Žã‹ã‚‰čĒŦ明しãĻおきぞす。React Hooks ã¯ã€ä¸€č¨€ã§č¨€ãˆã°é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトからäŊŋえる新しい APIです。これぞで、é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトはåŧ•æ•°ã¨ã—ãĻ props を受け取り、ãƒŦãƒŗダãƒĒãƒŗグįĩæžœã‚’čŋ”り値でčŋ”すというイãƒŗã‚ŋãƒŧフェãƒŧ゚で厚įžŠã•ã‚Œã€ã‚šãƒ†ãƒŧトãĒãŠãŽč¤‡é›‘ãĒ抟čƒŊを持たãĒい単į´”ãĒã‚ŗãƒŗポãƒŧネãƒŗトをäŊœã‚Œã‚‹ã‚‚ぎとしãĻ提䞛されãĻいぞした。初期ãĢは Stateless Function ComponentīŧˆįŠļ態ãĒしé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトīŧ‰ã¨ã„うį”¨čĒžãŒäŊŋわれãĻいたことからも分かる通り、もともとé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトは、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトが持つようãĒstateぎ抟čƒŊやcomponentDidMountを始めとするナイフã‚ĩイクãƒĢãƒĄã‚Ŋãƒƒãƒ‰ã‚’æŒãĄãžã›ã‚“ã§ã—ãŸã€‚

React Hooks ãĢよãŖãĻ、é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトãĢこれらぎ抟čƒŊを持たせることができるようãĢãĒりぞす。すãĒã‚ãĄã€åž“æĨはクナ゚ã‚ŗãƒŗポãƒŧネãƒŗトでしかå‡ēæĨãĒかãŖたことがé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトでもできるようãĢãĒるぎです。ただ一つæŗ¨æ„ã—ãĻいただきたいぎは、React Hooks はクナ゚ã‚ŗãƒŗポãƒŧネãƒŗトぎ API をただį§ģ植したわけではãĒいということです。React Hooks ぎ API は新しくデã‚ļイãƒŗしį›´ã•ã‚ŒãŸ API であり、垓æĨぎ API ãŽå•éĄŒį‚šãŒč§Ŗæļˆã•ã‚ŒãĻいぞす。䞋えば、フックぎ刊į”¨æ–šæŗ•ã‚’ただぎé–ĸ数å‘ŧãŗå‡ēしとすることãĢよãŖãĻ、フックぎ合成īŧˆīŧã‚Ģã‚šã‚ŋムフックぎäŊœæˆīŧ‰ã‚’厚易ãĢしãĻいぞすīŧˆã‚Ģã‚šã‚ŋムフックãĢついãĻはį­†č€…ぎäģĨå‰ãŽč¨˜äē‹ãŒå‚č€ƒãĢãĒるかもしれぞせんīŧ‰ã€‚

React Hooks は、API 上はuseから始ぞる名前ぎただぎé–ĸ数です。reactãƒ‘ãƒƒã‚ąãƒŧジからuseStateやuseEffectãĒおぎé–ĸ数がエク゚ポãƒŧトされãĻいぞす。čŖã§ã¯ React がäģŠãŠãŽã‚ŗãƒŗポãƒŧネãƒŗトをå‡Ļį†ã—ãĻいるかといãŖãŸæƒ…å ąã‚’äŋæŒã—ãĻおり、これらぎé–ĸæ•°ã¯ããŽæƒ…å ąãĢåŸēãĨいãĻ遊切ãĢ動äŊœã—ぞす。įžåœ¨ãŽã¨ã“ろ、React Hooks ぎ API はé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗト専į”¨ã§ã‚り、それäģĨå¤–ãŽã‚ˇãƒãƒĨエãƒŧã‚ˇãƒ§ãƒŗでå‘ŧãŗå‡ēすことはできぞせん。


åŸēæœŦぎフック

先čŋ°ãŽãƒ‰ã‚­ãƒĨãƒĄãƒŗトでは、useState, useEffect, useContextぎ 3 つがåŸēæœŦįš„ãĒフックとしãĻ挙げられãĻいぞす。それãĢå€ŖãŖãĻここでもぞずはこぎ 3 つをį´šäģ‹ã—ぞす。


useState

useStateフックは、é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトãĢ゚テãƒŧトを持たせられる API です。こぎフックをäŊŋうと、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトãĢおけるthis.stateãĢ値をäŋå­˜ã—ãĻおくぎと同じようãĒ感じで、ã‚ŗãƒŗポãƒŧネãƒŗトãĢįŠļ態を持たせることができぞす。äŊŋい斚はconst [stateぎ値, state更新é–ĸ数] = useState(state初期値);というぎがåŸēæœŦです。


useState ぎã‚ĩãƒŗプãƒĢ

さãŖそくですが、useStateぎäŊŋį”¨äž‹ã‚’čĻ‹ãĻãŋぞしょう。よくある䞋ですが、ボã‚ŋãƒŗをæŠŧすと数値がåĸ—えたり減ãŖたりするやつです。


useStateぎã‚ĩãƒŗプãƒĢ

import * as React from 'react';

const { useState } = React;

export const UseStateSample = () => {
const [count, setCount] = useState(0);

return (
<p>
<button onClick={() => setCount(count - 1)}>-</button>
<b>{count}</b>
<button onClick={() => setCount(count + 1)}>+</button>
</p>
);
};


こぎuseStateSampleはé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトで、į‰šãĢ props を受け取らずãĢ render 内厚をčŋ”すという垓æĨおおりぎイãƒŗã‚ŋãƒŧフェãƒŧ゚を持ãŖãĻいぞす。ポイãƒŗトは、そぎå‡Ļį†ãŽä¸­ã§useStateé–ĸ数をå‘ŧãŗå‡ēしãĻいるį‚šã§ã™ã€‚

äģŠå›žã¯countという゚テãƒŧトを初期値0でį”¨æ„ã—ãĻいぞす。よãŖãĻ、<b>{count}</b>ぎところは最初は 0 ãŒčĄ¨į¤ēされぞす。2 つぎボã‚ŋãƒŗをクãƒĒックすると、setCountがå‘ŧãŗå‡ēされるようãĢãĒãŖãĻいぞす。setCountをå‘ŧãŗå‡ēã—ãŸå ´åˆã¯æ¸Ąã—ãŸåŧ•æ•°ã§count゚テãƒŧトが更新されぞす。つぞり、UseStateSampleぎ再renderå‡Ļį†ãŒį™ēį”Ÿã—、そぎ際useStateãĢよãŖãĻčŋ”されるcountぎ値が新しい値とãĒãŖãĻいぞす。


クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトとぎ比čŧƒ

上ぎã‚ĩãƒŗプãƒĢと同じå‡Ļį†ã‚’æ•ĸえãĻ旧æĨぎクナ゚ã‚ŗãƒŗポãƒŧネãƒŗトで書くとこんãĒ感じです。

export class UseStateSample extends React.Component {

state = { count: 0 };
render() {
const { count } = this.state;
return (
<p>
<button onClick={() => this.setState({ count: count - 1 })}>-</button>
<b>{count}</b>
<button onClick={() => this.setState({ count: count + 1 })}>+</button>
</p>
);
}
}

é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトと Hooks で書いた場合ãĢ比ずãĻį…Šé›‘ですね。そぎčĻå› ã¨ã—ãĻは、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトではthis.stateがã‚Ēブジェクトであることや、this.setStateがそぎã‚Ēãƒ–ã‚¸ã‚§ã‚¯ãƒˆãŽã†ãĄä¸€éƒ¨ã‚’ã‚ĸップデãƒŧトするようãĒ API 設計ãĢãĒãŖãĻいることが挙げられぞす。React Hooks ぎ API ではcountという゚テãƒŧトãĢ寞しãĻsetCountという専į”¨ãŽé–ĸ数がã‚ģットでį”¨æ„ã•ã‚Œãžã™ã‹ã‚‰ã€{count: count+1}ぎようãĒäŊ™č¨ˆãĒã‚Ēブジェクトį”ŸæˆãŒãĒくį›´æ„Ÿįš„かつすãŖきりと書くことができãĻいぞす。


č¤‡æ•°ãŽã‚šãƒ†ãƒŧトをäŊŋう

é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトでは、useStateã‚’č¤‡æ•°å›žå‘ŧãļã“ã¨ã§č¤‡æ•°ãŽã‚šãƒ†ãƒŧトを刊į”¨ã™ã‚‹ã“とができぞす。そぎ場合、それぞれぎ゚テãƒŧトãĢ寞しãĻåˆĨ々ぎ更新é–ĸ数が垗られぞす。


č¤‡æ•°ãŽã‚šãƒ†ãƒŧトをį”¨ã„ã‚‹äž‹

export const UseStateSample2 = () => {

const [left, setLeft] = useState(0);
const [right, setRight] = useState(0);

return (
<p>
<b>{left}</b>
<button
onClick={() => {
setLeft(left + 1);
setRight(right - 1);
}}
>
←
</button>
<b>{right}</b>
<button onClick={() => setRight(right + 1)}>+</button>
</p>
);
};


こぎ䞋ではã‚ŗãƒŗポãƒŧネãƒŗトはleftとrightという 2 つぎ゚テãƒŧトを持ãŖãĻいぞす。それぞれぎ゚テãƒŧトをã‚ĸップデãƒŧトするãĢは寞åŋœã™ã‚‹é–ĸ数をį”¨ã„ぞす。įœŸã‚“中あたりãĢčĻ‹ãˆã‚‹ã‚ˆã†ãĢã€č¤‡æ•°ãŽã‚šãƒ†ãƒŧトを同時ãĢ更新するãĢはé–ĸ数を全部å‘ŧãŗぞす。

これをčĻ‹ã‚‹ã¨ã€ã‚šãƒ†ãƒŧãƒˆãŒč¤‡é›‘ãĢãĒãŖãĻきたときãĢ゚テãƒŧトぎ更新が一į™ēでできずãĢたくさんぎé–ĸ数å‘ŧãŗå‡ēしがåŋ…čĻãĢãĒãŖãĻしぞうという懸åŋĩがあるかもしれぞせん。そぎ場合ぱテãƒŧトぎ値を数値ãĒおではãĒくã‚ĒブジェクトãĢしãĻ一į™ēで更新するãģうがよいことがありぞす。こぎやり斚を支援するフックとしãĻuseReducerがありぞすぎで、あとでį´šäģ‹ã—ぞす。


é–ĸ数ãĢよる゚テãƒŧトぎ更新

゚テãƒŧトぎ更新é–ĸ数īŧˆä¸ŠãŽäž‹ã§ãŽsetCountãĒおīŧ‰ã‚’å‘ŧãļ場合、äģŠãžã§ãŽã‚ĩãƒŗプãƒĢでは新しい゚テãƒŧãƒˆãŽå€¤ã‚’æ¸Ąã—ãĻã„ãžã—ãŸã€‚ä¸‹č¨˜ãŽäž‹ã§ã¯ã€ã“ãŽãƒœã‚ŋãƒŗをæŠŧすとcount゚テãƒŧトを 1 åĸ—やす、つぞり新しい値としãĻcount + 1をã‚ģットしãĻいることが分かりぞす。

<button onClick={() => setCount(count + 1)}>+</button>

原は、更新é–ĸ数ãĢはé–ĸæ•°ã‚’æ¸Ąã™ã“ã¨ãŒã§ããžã™ã€‚ããŽå ´åˆã€ã“ã‚Œã¯įžåœ¨ãŽã‚šãƒ†ãƒŧトぎ値を受け取ãŖãĻ新しい゚テãƒŧトぎ値をčŋ”すé–ĸ数としãĻč§Ŗ釈されぞす。つぞり、上ぎ䞋はこぎようãĢ書き換えることができぞす。

<button onClick={() => setCount(count => count + 1)}>+</button>

これはsetCountãĢé–ĸæ•°ã‚’æ¸Ąã™ã“ã¨ã§ã€ã€Œįžåœ¨ãŽã‚šãƒ†ãƒŧトをčĒ­ã‚“で、それãĢ 1 をčļŗした値を新しい゚テãƒŧトぎ値とする」ということを指į¤ēしãĻいぞす。こぎようãĢ、æŦĄãŽã‚šãƒ†ãƒŧトぎ値がįžåœ¨ãŽã‚šãƒ†ãƒŧトãĢ䞝存する場合īŧˆã™ãĒã‚ãĄįžåœ¨ãŽã‚šãƒ†ãƒŧトから新しい゚テãƒŧãƒˆã‚’č¨ˆįŽ—する場合īŧ‰ã¯é–ĸ数をį”¨ã„た更新が遊しãĻいぞす。

そぎį†į”ąãŽ 1 つは、こぎ斚æŗ•ã ã¨ã‚šãƒ†ãƒŧトぎ更新ロジックをį´”į˛‹é–ĸ数ãĢ抜きå‡ēすことができることです。さらãĢもう 1 つぎį†į”ąã¨ã—ãĻ、é–ĸ数をäŊŋわãĒいとã‚ŗãƒŧãƒĢãƒãƒƒã‚¯ãŒč¤‡æ•°å›žå‘ŧばれる場合ãĢæ­Ŗしく寞å‡ĻできãĒいことがありぞす。

䞋としãĻ、「1 回æŠŧすと onClick が 5 回į™ēį”Ÿã™ã‚‹ãƒœã‚ŋãƒŗ」というã‚ŗãƒŗポãƒŧネãƒŗトSuperButtonをäŊœãŖãĻãŋぞした。こぎボã‚ŋãƒŗをäŊŋうと 2 つぎ斚æŗ•ãŽé•ã„が分かりぞす。

const SuperButton = ({ onClick, children }) => {

const onclickHere =
onClick &&
(e => {
for (const _ of [0, 1, 2, 3, 4]) onClick(e);
});
return <button onClick={onclickHere}>{children}</button>;
};
export const UseStateSample4 = () => {
const [count, setCount] = useState(0);

return (
<p>
<SuperButton onClick={() => setCount(count - 1)}>-</SuperButton>
<b>{count}</b>
<SuperButton onClick={() => setCount(count => count + 1)}>+</SuperButton>
</p>
);
};

こぎ䞋では、SuperButtonぎ onClick é–ĸ数で 2 į¨ŽéĄžãŽæ–šæŗ•ã§æ›¸ã„た゚テãƒŧトぎ更新をį™ēį”Ÿã•ã›ãĻいぞす。æ—ĸãĢお察しかと思いぞすが、() => setCount(count - 1)ぎãģうは、これが 5 回é€Ŗįļšã§å‘ŧãŗå‡ēされãĻもcountは 1 だけ減りぞす。ãĒぜãĒら、setCount(count - 1)で参į…§ã•ã‚Œã‚‹countは常ãĢこぎUseStateSample4ã‚ŗãƒŗポãƒŧネãƒŗトがãƒŦãƒŗダãƒĒãƒŗグされたときぎcountだからです。

それãĢ寞し、()=> setCount(count => count + 1)ぎ場合は、これが 5 回å‘ŧãŗå‡ēされることでcountは 5 åĸ—えぞす。これは、こぎé–ĸ数がå‘ŧãŗå‡ēされるたãŗãĢįžåœ¨ãŽcountぎ値が参į…§ã•ã‚Œã‚‹īŧˆãã—ãĻそれãĢ 1 をčļŗした数値が新しいcountとãĒるīŧ‰ã‹ã‚‰ã§ã™ã€‚

こぎようãĢ、é–ĸ数がワãƒŗãƒ‘ã‚šã§č¤‡æ•°å›žå‘ŧãŗå‡ēされることをæƒŗ厚すると、é–ĸ数をį”¨ã„た゚テãƒŧト更新īŧˆä¸ŠãŽäž‹ã§č¨€ãˆã°åžŒč€…īŧ‰ãĢしãĒいとæƒŗ厚した動äŊœãĢãĒらãĒいことがありぞす。įš°ã‚Ščŋ”しぞすが、įžåœ¨ãŽã‚šãƒ†ãƒŧトãĢ䞝存しãĻæ›´æ–°ã‚’čĄŒã†ãĒらこぎようãĢé–ĸ数を゚テãƒŧト更新é–ĸ数ãĢæ¸Ąã—ãžã—ã‚‡ã†ã€‚


useEffect

useStateとä¸ĻんでよくäŊŋうであろうフックがこぎuseEffectフックです。これはãƒŦãƒŗダãƒĒãƒŗグ垌ãĢčĄŒã†å‡Ļį†ã‚’指厚できるフックです。クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトぎナイフã‚ĩイクãƒĢでいえば、componentDidMount及ãŗcomponentDidUpdateãĢおおよそį›¸åŊ“するもぎですīŧˆåŽŸéš›ãĢは多少違うぎですが、それは垌でčĒŦ明しぞすīŧ‰ã€‚

䞋えば、äģĨ下ぎ䞋は 1 į§’ごとãĢ襨į¤ēされãĻいる値が 1 åĸ—えるã‚ŗãƒŗポãƒŧネãƒŗトです。įžåœ¨čĄ¨į¤ēしãĻいる値を゚テãƒŧトでįŽĄį†ã™ã‚‹ãŸã‚ãĢ、さãŖきį´šäģ‹ã—たuseStateとįĩ„ãŋ合わせãĻ原čŖ…しãĻいぞす。

import * as React from 'react';

const { useState, useEffect } = React;

export const UseEffectSample1 = () => {
const [count, setCount] = useState(0);

useEffect(() => {
const timerId = setTimeout(() => {
setCount(count => count + 1);
}, 1000);
// クãƒĒãƒŧãƒŗã‚ĸップé–ĸ数をčŋ”す
return () => clearTimeout(timerId);
}, [count]);

return (
<p>
time: <b>{count}</b>
</p>
);
};

こぎようãĢ、useEffectは 2 つぎåŧ•æ•°ã‚’取りぞす。čŋ”り値はありぞせん。ぞた、2 つį›ŽãŽåŧ•æ•°ã¯įœį•Ĩ可čƒŊです。1 つį›ŽãŽåŧ•æ•°ã¯ã‚ŗãƒŧãƒĢバックé–ĸ数であり、こぎé–ĸ数はã‚ŗãƒŗポãƒŧネãƒŗトぎãƒŦãƒŗダãƒĒãƒŗグ厌äē†æ™‚ãĢå‘ŧばれぞす。最初ぎãƒŦãƒŗダãƒĒãƒŗã‚°ã¯ã‚‚ãĄã‚ã‚“ã€å†ãƒŦãƒŗダãƒĒãƒŗグがį™ēį”Ÿã—たあとãĢもå‘ŧばれぞす。ただし、įŦŦ 2 åŧ•æ•°ã§ã‚ŗãƒŧãƒĢバックé–ĸ数をå‘ŧãļã‚ŋイミãƒŗグをåˆļåžĄã§ããžã™ã€‚įŦŦ 2 åŧ•æ•°ã¯å€¤ãŽé…åˆ—であり、配列ぎいずれかぎ値が前回と変わãŖたときぎãŋã‚ŗãƒŧãƒĢバックé–ĸ数をå‘ŧãļという意å‘ŗãĢãĒりぞす。įœį•Ĩした場合はį„ĄæĄäģļ、つぞりãƒŦãƒŗダãƒĒãƒŗグぎåēĻãĢé–ĸ数がå‘ŧばれぞす2。

上ぎã‚ĩãƒŗプãƒĢでは、ぞず最初ぎãƒŦãƒŗダãƒĒãƒŗグが厌äē†ã—たã‚ŋイミãƒŗグでuseEffectãĢ指厚したã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧãŗだされぞす。それãĢより、1 į§’垌ãĢcount゚テãƒŧトが更新されぞす。それãĢよりã‚ŗãƒŗポãƒŧネãƒŗトぎ再ãƒŦãƒŗダãƒĒãƒŗグがį™ēį”Ÿã—、再ãŗuseEffectぎã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれぞす。こぎįš°ã‚Ščŋ”しãĢより、こぎã‚ŗãƒŗポãƒŧネãƒŗトは 1 į§’ごとãĢ数値が 1 ずつåĸ—えãĻいくã‚ŗãƒŗポãƒŧネãƒŗトとãĒりぞす。

äģŠå›žįŦŦ 2 åŧ•æ•°ã¯[count]です。つぞり、ãƒŦãƒŗダãƒĒãƒŗグįĩ‚äē†æ™‚ãĢ、countぎ値が前回ぎãƒŦãƒŗダãƒĒãƒŗグと変わãŖãĻいたらuseEffectぎã‚ŗãƒŧãƒĢバックをį™ēįĢするということãĢãĒりぞす。äģŠå›žãŽå ´åˆã¯ã‚šãƒ†ãƒŧトがこれだけãĒぎでįœį•ĨしãĻも同じですが、äģ–ãĢも゚テãƒŧトがある場合ãĢäŊ™č¨ˆãĒå‡Ļį†ãŒį™ēį”Ÿã—ãĒいようãĢする劚果がありぞす。

ここでå‘ŧばれãĻいるã‚ŗãƒŧãƒĢバックé–ĸ数は、よくčĻ‹ã‚‹ã¨é–ĸ数をæˆģり値としãĻいぞす。これはクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数です。クãƒĒãƒŧãƒŗã‚ĸップé–ĸ数をåŽŖč¨€ã—ãŸå ´åˆã¯ã€æŦĄå›žãŽã‚ŗãƒŧãƒĢバックがå‘ŧばれる前ãĢクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数がå‘ŧばれぞす。ぞた、ã‚ŗãƒŗポãƒŧネãƒŗトがã‚ĸãƒŗマã‚Ļãƒŗトされる前ãĢもクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数がå‘ŧばれぞす。čĻã™ã‚‹ãĢ、描į”ģ時ãĢã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれた場合、そぎ描į”ģがæļˆã•ã‚Œã‚‹ã¨ããĢ寞åŋœã™ã‚‹ã‚¯ãƒĒãƒŧãƒŗã‚ĸップé–ĸ数がå‘ŧばれるということです。とãĻもäžŋ刊ですね。

上ぎ䞋では、æ­ŖįĸēãĢは初回ãƒŦãƒŗダãƒĒãƒŗグ →useEffectぎã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれる →1 į§’垌ãĢ゚テãƒŧトが変更されãĻ再ãƒŦãƒŗダãƒĒãƒŗグがį™ēį”Ÿ →クãƒĒãƒŧãƒŗã‚ĸップé–ĸ数がå‘ŧばれる→useEffectぎã‚ŗãƒŧãƒĢバックé–ĸ数が再ãŗå‘ŧばれる というæĩã‚Œã‚’とãŖãĻいることãĢãĒりぞす。ここではクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数でclearTimeoutをå‘ŧんでいぞすが、こぎようãĢã‚ŗãƒŧãƒĢバックé–ĸ数でį™ēį”Ÿã—た副äŊœį”¨ãŽåžŒå§‹æœĢをするぎがクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数ぎä¸ģãĒåŊšį›Žã§ã™ã€‚こぎクãƒĒãƒŧãƒŗã‚ĸップé–ĸ数はsetTimeoutぎį™ēįĢ垌ãĢå‘ŧばれた場合は意å‘ŗがありぞせんが、ã‚ŗãƒŗポãƒŧネãƒŗトがã‚ĸãƒŗマã‚ĻãƒŗトされãĻぞだsetTimeoutがį™ēįĢしãĻいãĒい場合ãĢ、すでãĢã‚ĸãƒŗマã‚Ļãƒŗトされたã‚ŗãƒŗポãƒŧネãƒŗトぎ゚テãƒŧトを変更しãĻã—ãžã†ãŽã‚’é˜˛ãæ„å‘ŗがありぞす。


useContext

useContextは、指厚したã‚ŗãƒŗテキ゚トぎįžåœ¨ãŽå€¤ã‚’垗るフックです。ã‚ŗãƒŗテキ゚トというぎは React 16.3 で搭čŧ‰ã•ã‚ŒãŸæ–°ã—いã‚ŗãƒŗテキ゚ト API ãĢよるもぎを指しãĻいぞす。React.createContextでäŊœæˆã—たã‚ŗãƒŗテキ゚トã‚ĒブジェクトをuseContextãĢæ¸Ąã™ã“ã¨ã§ã€ããŽã‚ŗãƒŗテキ゚トぎįžåœ¨ãŽå€¤ã‚’čŋ”り値で垗ることができぞす。


useContextぎã‚ĩãƒŗプãƒĢ

import * as React from 'react';

const { useState, useContext, createContext } = React;

const MyContext = createContext(() => {});

export const UseContextSample = () => {
const [count, setCount] = useState(0);

return (
<div>
<p>
<b>{count}</b>
</p>
<MyContext.Provider value={() => setCount(count => count + 1)}>
<IncrementButton />
</MyContext.Provider>
</div>
);
};

const IncrementButton = () => {
const incrementHandler = useContext(MyContext);

return (
<p>
<button onClick={incrementHandler}>+</button>
</p>
);
};


こぎ䞋はį›¸å¤‰ã‚ã‚‰ãšãƒœã‚ŋãƒŗをæŠŧすと数値がåĸ—えるという単į´”ãĒã‚ĩãƒŗプãƒĢですが、数値をåĸ—やすボã‚ŋãƒŗをIncrementButtonというåˆĨぎã‚ŗãƒŗポãƒŧネãƒŗトãĢį§ģしぞした。そうãĒると、ボã‚ŋãƒŗをæŠŧしたときぎå‡Ļį†īŧˆUseContextSampleぎ゚テãƒŧトを変更するīŧ‰ã‚’IncrementButtonã‚ŗãƒŗポãƒŧネãƒŗトãĢäŧãˆã‚‹åŋ…čĻãŒã‚りぞす。一į•Ē単į´”ãĒæ–šæŗ•ã¯ props ãŽã˛ã¨ã¤ã¨ã—ãĻそぎé–ĸæ•°ã‚’æ¸Ąã™ã“ã¨ã§ã™ãŒã€äŊ•æŽĩ階もäŧæ’­ã•ã›ãĒいといけãĒいようãĒ場合ãĢは props をäŊŋうぎは遊しãĻいãĒいことがありぞす。そぎ場合はこぎ䞋ぎようãĢã‚ŗãƒŗテキ゚トをäŊŋãŖãĻ暗éģ™ãĢ値をäŧæ’­ã•ã›ãžã™ã€‚

IncrementButtonã‚’åž“æĨぎ API で書きį›´ã™ã¨ã“ã‚“ãĒ感じです。ã‚ŗãƒŗポãƒŧネãƒŗトぎネ゚トがį„ĄããĒãŖãĻįļēéē—ãĢ書けるぎがåŦ‰ã—いですね。

const IncrementButton = () => {

return (
<MyContext.Consumer>
{incrementHandler => (
<p>
<button onClick={incrementHandler}>+</button>
</p>
)}
</MyContext.Consumer>
);
};


そぎãģかぎフック

ここぞでぎ 3 つがとãĻã‚‚č‰¯ãäŊŋいそうãĒフックでした。それäģĨ外ぎフックもįļšã‘ãĻį´šäģ‹ã—ãĻいきぞす。比čŧƒįš„よくäŊŋいそうãĒフックからč§ŖčĒŦしãĻいきぞす。


useReducer

useReducerは useState ぎäēœį¨Žã§ã‚り、゚テãƒŧトをåŽŖč¨€ã™ã‚‹ãƒ•ãƒƒã‚¯ã§ã™ã€‚useReducerは、゚テãƒŧトぎ初期įŠļ態ãĢ加えãĻreducer とå‘ŧばれるé–ĸæ•°ã‚’æ¸Ąã—ãžã™ã€‚įĩæžœã¨ã—ãĻ、įžåœ¨ãŽã‚šãƒ†ãƒŧトãĢ加えãĻ、゚テãƒŧトを更新するé–ĸ数ぎäģŖわりãĢ dispatch é–ĸ数が垗られぞす。こぎ reducer や dispatch というぎは Redux į”¨čĒžã¨ã—ãĻよくįŸĨられãĻいぞすぎで、Redux をč§ĻãŖたことがあるかたはéĻ´æŸ“ãŋæˇąã„æĻ‚åŋĩでしょう。

useReducerãĢよãŖãĻäŊœã‚‰ã‚ŒãŸã‚šãƒ†ãƒŧトを更新する場合は、dispatché–ĸ数ãĢã‚ĸã‚¯ã‚ˇãƒ§ãƒŗã‚’æ¸Ąã—ãžã™ã€‚ã‚ĸã‚¯ã‚ˇãƒ§ãƒŗというぎぱテãƒŧトぎ更新指į¤ēã‚’čĄ¨ã™å€¤ã§ã€åˆĨãĢãĒんでも構いぞせん。そぎã‚ĸã‚¯ã‚ˇãƒ§ãƒŗをåŧ•æ•°ã¨ã—ãĻ、useReducerãĢæ¸Ąã—ãŸ reducer がå‘ŧãŗå‡ēされぞす。reducer というぎはé–ĸ数であり、ã‚ĸã‚¯ã‚ˇãƒ§ãƒŗとįžåœ¨ãŽã‚šãƒ†ãƒŧトを受け取ãŖãĻ新しい゚テãƒŧトをčŋ”しぞす。


useReducerぎ䞋

import * as React from 'react';

const { useContext, useReducer, createContext } = React;

const DispatchContext = createContext(() => {});

const reducer = ({ year, month }, action) => {
switch (action) {
case 'increment':
return month === 11
? { year: year + 1, month: 0 }
: { year, month: month + 1 };
case 'decrement':
return month === 0
? { year: year - 1, month: 11 }
: { year, month: month - 1 };
}
};

export const UseReducerSample = () => {
const [state, dispatch] = useReducer(reducer, {
year: 0,
month: 1,
});

return (
<div>
<p>
<b>
{state.year}åš´{state.month}ãƒļ月
</b>
</p>
<DispatchContext.Provider value={dispatch}>
<ControlButtons />
</DispatchContext.Provider>
</div>
);
};

const ControlButtons = () => {
const dispatch = useContext(DispatchContext);

return (
<p>
<button onClick={() => dispatch('decrement')}>-</button>
<button onClick={() => dispatch('increment')}>+</button>
</p>
);
};


こぎ䞋はこぎようãĢãƒŦãƒŗダãƒĒãƒŗグされぞす。

Image from Gyazo

こぎ䞋ぎようãĢ、useReducerãĢは 2 つぎåŧ•æ•°ã‚’æ¸Ąã—ãžã™ã€‚1 つį›ŽãŽåŧ•æ•°ã¯ reducer で、2 つį›ŽãŽåŧ•æ•°ã¯åˆæœŸįŠļ態です。äģŠå›žuseReducerでåŽŖč¨€ã™ã‚‹ã‚šãƒ†ãƒŧトはyearとmonthという 2 つぎプロパテã‚Ŗを持ãŖたã‚Ēブジェクトです。+ボã‚ŋãƒŗと-ボã‚ŋãƒŗをæŠŧすと、月が 1 つåĸ—えぞす。12 ãƒļ月ãĢãĒると嚴が 1 つåĸ—えãĻ月が 0 ãĢæˆģりぞす。īŧˆã‚くぞでã‚ĩãƒŗプãƒĢãĒぎで、嚴とか 12 ã§å‰˛ãŖãĻ計įŽ—しろよという扚判は受けäģ˜ã‘ぞせん。īŧ‰

ボã‚ŋãƒŗがæŠŧされたときぎロジックがreduceré–ĸ数ãĢぞとぞãŖãĻいることが分かりぞすね。reducerぎįŦŦ 1 åŧ•æ•°ãŒäģŠãŽįŠļ態で、įŦŦ 2 åŧ•æ•°ãŒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗīŧˆå˜į´”ãĒ例ãĒぎでäģŠå›žã¯æ–‡å­—列īŧ‰ã§ã™ã€‚そしãĻ、useReducerãĢよりåŽŖč¨€ã•ã‚ŒãŸã‚šãƒ†ãƒŧトぎ更新は、dispatché–ĸ数ãĢã‚ĸã‚¯ã‚ˇãƒ§ãƒŗã‚’æ¸Ąã™ã“ã¨ã§čĄŒã„ãžã™ã€‚dispatchを子ã‚ŗãƒŗポãƒŧネãƒŗトであるControlButtonsãĢæ¸Ąã™ãŽã¯å…ˆãģおį´šäģ‹ã—たuseContextをäŊŋいぞした。

äģŠå›žãŽãƒã‚¤ãƒŗトは、+ボã‚ŋãƒŗをæŠŧすとyearとmonthãŒä¸Ąæ–šåŒæ™‚ãĢ変化する可čƒŊ性があるということです。こぎようãĒ複雑ãĒ変化をã‚ĸã‚¯ã‚ˇãƒ§ãƒŗというæŠŊ蹥įš„ãĒå‘Ŋäģ¤ã§čĄ¨ã™ã“とãĢよãŖãĻ、゚テãƒŧトを変化させる側īŧˆdispatchをå‘ŧãŗå‡ēす側īŧ‰ãŽå˜į´”化と゚テãƒŧト変更ロジックぎ分é›ĸを同時ãĢ達成しãĻいぞす。useReducerをį”¨ã„ãĻč‰˛ã€…ãĒįŠļæ…‹ã‚’ã˛ã¨ãžã¨ã‚ãĢåŽŖč¨€ã™ã‚‹å ´åˆã€č‰˛ã€…ãĒ子ã‚ŗãƒŗポãƒŧネãƒŗトがdispatchをį”¨ã„ãĻįŠļ態を変化させるはずですから、こぎ䞋ぎようãĢuseContextとįĩ„ãŋ合わせãĻdispatché–ĸ数を子ã‚ŗãƒŗポãƒŧネãƒŗãƒˆãŸãĄãĢäŧãˆã‚‹ãŽãŒį‰šãĢ遊しãĻいぞす。゚テãƒŧトぎ更新はすずãĻdispatchを通じãĻčĄŒã†ãŸã‚ã€dispatché–ĸæ•°ã˛ã¨ã¤äŧãˆã‚Œã°ååˆ†ã§ã‚るというぎもåŦ‰ã—いį‚šã§ã™ã€‚


é–ĸ数ãĢよる゚テãƒŧトぎ初期化

useReducerぎįŦŦ 2 åŧ•æ•°ãĢ初期゚テãƒŧãƒˆã‚’æ¸Ąã™äģŖわりãĢ、é–ĸ数をį”¨ã„ãĻ゚テãƒŧトを初期化することができぞす。こぎ場合、useReducerぎįŦŦ 3 åŧ•æ•°ãĢ初期化é–ĸæ•°ã‚’æ¸Ąã—ãžã™ã€‚ãžãŸã€įŦŦ 2 åŧ•æ•°ãŽæ„å‘ŗãŒå¤‰ã‚ã‚Šãžã™ã€‚æ¸Ąã—ãŸåˆæœŸåŒ–é–ĸ数ぎåŧ•æ•°ã¨ã—ãĻuseReducerįŦŦ 2 åŧ•æ•°ãŒæ¸Ąã•ã‚Œãžã™ã€‚こぎ抟čƒŊは、゚テãƒŧトぎ初期化ロジックをåˆĨぎé–ĸ数としãĻ厚įžŠã—たい場合ãĒおãĢäžŋ刊です。


useReducerぎįŦŦ3åŧ•æ•°ãŽäž‹

const reducer = ({ year, month }, action) => {

switch (action) {
case 'increment':
return month === 11
? { year: year + 1, month: 0 }
: { year, month: month + 1 };
case 'decrement':
return month === 0
? { year: year - 1, month: 11 }
: { year, month: month - 1 };
}
};

const init = initialMonth => ({
year: 0,
month: initialMonth,
});

export const UseReducerSample2 = ({ initialMonth }) => {
const [state, dispatch] = useReducer(reducer, initialMonth, init);

return (
<div>
<p>
<b>
{state.year}åš´{state.month}ãƒļ月
</b>
</p>
<DispatchContext.Provider value={dispatch}>
<ControlButtons />
</DispatchContext.Provider>
</div>
);
};


こぎ䞋では、゚テãƒŧトぎ初期化é–ĸ数initを厚įžŠã—ãĻuseReducerぎįŦŦ 3 åŧ•æ•°ãĢæ¸Ąã—ãžã—ãŸã€‚ã“ãŽã‚ŗãƒŗポãƒŧネãƒŗトを<UseReducerSample2 initialMonth={10} />ぎようãĢäŊŋうと、「0 åš´ 10 ãƒļæœˆã€ãŽčĄ¨į¤ēから゚ã‚ŋãƒŧトしぞす。こぎようãĢ、įŦŦ 2 åŧ•æ•°ã‚’初期゚テãƒŧトではãĒく゚テãƒŧトぎ元とãĒる値ãĢしãĻ、įŦŦ 3 åŧ•æ•°ãĢæ¸Ąã—ãŸinité–ĸ数でそれを゚テãƒŧトãĢ変換するという斚åŧã‚’とãŖãĻいぞす。

こぎ場合はįŦŦ 2 åŧ•æ•°ã‚’{year: 0, month: initialMonth}とするという手もありぞすが、初期゚テãƒŧトがã‚ŗãƒŗポãƒŧネãƒŗトぎ中で厚įžŠã•ã‚ŒãĻいるぎはおうも垎åĻ™ãĢ思えぞす。そういう時ãĢįŦŦ 3 åŧ•æ•°ã‚’äŊŋいぞしょう。


useMemo

useMemoã¯ã€ããŽåãŽé€šã‚Šå€¤ãŽãƒĄãƒĸ化ãĢäŊŋえるフックです。įŦŦ 1 åŧ•æ•°ãĢå€¤ã‚’č¨ˆįŽ—するé–ĸ数を、įŦŦ 2 åŧ•æ•°ãĢ計įŽ—される値が䞝存する値ぎ一čĻ§ã‚’æ¸Ąã—ãžã™ã€‚


useMemoぎ䞋

import * as React from 'react';

const { useMemo } = React;

export const UseMemoSample = ({ n }) => {
const sum = useMemo(() => {
let result = 0;
for (let i = 1; i <= n; i++) {
result += i;
}
return result;
}, [n]);

return (
<div>
<p>
1 + â€Ļ + n = <b>{sum}</b>
</p>
</div>
);
};


ここで厚įžŠã—たUseMemoSampleは、1 ã‹ã‚‰æŒ‡åŽšã—ãŸå€¤ãžã§ãŽå’Œã‚’čĄ¨į¤ēするという意å‘ŗ不明ãĒã‚ŗãƒŗポãƒŧネãƒŗトです。しかも、そぎ和は for ãƒĢãƒŧプを回しãĻ計įŽ—しぞす。<UseMemoSample n={1000} />ぎようãĢäŊŋうと1 + â€Ļ + 1000 = 500500ã¨čĄ¨į¤ēしぞす。

こぎ䞋はともかく、ãƒĢãƒŧプとか回すå‡Ļį†ã‚’ render ぎ中ãĢベã‚ŋ書きすると、ãƒŦãƒŗダãƒĒãƒŗã‚°ãŒčĄŒã‚ã‚Œã‚‹ãŸãŗãĢãã‚ŒãŒč¨ˆįŽ—されることãĢãĒã‚Šãžã™ã€‚č¨ˆįŽ—įĩæžœã‚’ãƒĄãƒĸ化したい、すãĒã‚ãĄäģĨå‰č¨ˆįŽ—したįĩæžœãŒå†åˆŠį”¨ã§ãã‚‹ã¨ãã¯å†åˆŠį”¨ã—たい、という場合ãĢuseMemoがåŊšãĢįĢ‹ãĄãžã™ã€‚

useMemoぎįŦŦ 1 åŧ•æ•°ã¯ã€å€¤ã‚’č¨ˆįŽ—するé–ĸæ•°ã‚’æ¸Ąã—ãžã™ã€‚ãã‚Œč‡ĒčēĢãĢåŧ•æ•°ã¯ã‚りぞせんが、props ぎ値ãĒおをäŊŋį”¨ã—ãĻも構いぞせん。そぎé–ĸ数ぎčŋ”り値がuseMemoぎčŋ”り値とãĒりぞす。こぎé–ĸ数がå‘ŧãŗå‡ēã•ã‚Œã‚‹ãŽã¯å€¤ãŽč¨ˆįŽ—がåŋ…čĻã¨ãĒãŖたときです。つぞり、初回ぎãƒŦãƒŗダãƒĒãƒŗグ時およãŗå†č¨ˆįŽ—がåŋ…čĻã¨ãĒãŖたときãĢé–ĸ数がå‘ŧãŗå‡ēされぞす。useMemoはäģĨå‰ãŽč¨ˆįŽ—ぎįĩæžœã‚’čĻšãˆãĻãŠã‚Šã€å†č¨ˆįŽ—がåŋ…čĻãĒã„å ´åˆã¯æ¸Ąã—ãŸé–ĸ数はå‘ŧãŗå‡ēされず、äģĨå‰ãŽč¨ˆįŽ—ぎįĩæžœãŒčŋ”されぞす。

å†č¨ˆįŽ—がいつåŋ…čĻã‹ã¯useMemoぎįŦŦ 2 åŧ•æ•°ã§æŒ‡åŽšã—ぞす。これはuseEffectぎįŦŦ 2 åŧ•æ•°ã¨åŒã˜ã§ã€ã“こãĢæ¸Ąã—ãŸå€¤ãŽã„ãšã‚Œã‹ãŒå¤‰åŒ–ã—ãŸã¨ããĢå†č¨ˆįŽ—ãŒčĄŒãĒわれぞす。äģŠå›žãŽå ´åˆã¯č¨ˆįŽ—įĩæžœã¯nãĢ䞝存しãĻいるため、įŦŦ 2 åŧ•æ•°ãĢ[n]ã‚’æ¸Ąã™åŋ…čĻãŒã‚りぞす。これãĢより、nãŒå¤‰åŒ–ã—ãŸã¨ãå†č¨ˆįŽ—ãŒčĄŒãĒわれるようãĢãĒりぞす。


useCallback

useCallbackはuseMemoぎäēœį¨Žã§ã™ã€‚į°ĄæŊ”ãĢč¨€ãˆã°ã€useCallback(fn, arr)はuseMemo(()=> fn, arr)と同じです。つぞり、useCallbackã¯č¨ˆįŽ—ぎåŋ…čĻãĒã„å€¤ã‚’ãƒĄãƒĸ化するときãĢäžŋ刊ãĒフックです。

計įŽ—がåŋ…čĻãĒいぎãĢãƒĄãƒĸ化とはおういうことかとお思いかもしれぞせんが、useCallbackという名前がį¤ē唆するとおり、これはé–ĸæ•°ã‚’ãƒĄãƒĸ化するぎãĢäžŋ刊です。ポイãƒŗトは、()=> { ... }ぎようãĒé–ĸ数åŧã¯æ¯Žå›žæ–°ã—いé–ĸ数ã‚ĒブジェクトをäŊœã‚‹ã¨ã„うį‚šã§ã™ã€‚

äģĨ下ãĢuseContextぎã‚ĩãƒŗプãƒĢã‚’å†æŽ˛ã—ãžã™ã€‚

export const UseContextSample = () => {

const [count, setCount] = useState(0);

return (
<div>
<p>
<b>{count}</b>
</p>
<MyContext.Provider value={() => setCount(count => count + 1)}>
<IncrementButton />
</MyContext.Provider>
</div>
);
};

ここでMyContext.Providerぎ prop としãĻ()=> setCount(count => count+1)というé–ĸæ•°ã‚’æ¸Ąã—ãĻいぞす。これはé–ĸ数åŧãĒぎで、UseContextSampleがãƒŦãƒŗダãƒĒãƒŗグされるたãŗãĢ新しいé–ĸ数ã‚ĒブジェクトがäŊœã‚‰ã‚ŒãĻそれがMyContext.ProviderãĢæ¸Ąã•ã‚Œãžã™ã€‚åŽŸã¯ã“ã‚Œã¯č‰¯ãã‚ã‚Šãžã›ã‚“ã€‚ãĒぜãĒら、ã‚ŗãƒŗテキ゚トãĢæ¸Ąã•ã‚ŒãŸå€¤ãŒå¤‰åŒ–ã™ã‚‹ãŸãŗãĢそぎã‚ŗãƒŗテキ゚トぎ値をäŊŋį”¨ã™ã‚‹ã‚ŗãƒŗポãƒŧネãƒŗトは全部再描į”ģされるため、上ぎ䞋でMyContextをäŊŋį”¨ã™ã‚‹ã‚ŗãƒŗポãƒŧネãƒŗトが毎回再描į”ģされãĻしぞうからです。

こういう時はuseCallbackぎå‡ēį•Ēです。äģĨ下ぎようãĢすることで再描į”ģã‚’é˜˛ãã“ã¨ãŒã§ããžã™ã€‚


useCallbackぎã‚ĩãƒŗプãƒĢ

import * as React from 'react';

const { useState, useContext, useCallback, createContext } = React;

const MyContext = createContext(() => {});

export const UseCallbackSample = () => {
const [count, setCount] = useState(0);

const updateCount = useCallback(() => setCount(count => count + 1), []);

return (
<div>
<p>
<b>{count}</b>
</p>
<MyContext.Provider value={updateCount}>
<IncrementButton />
</MyContext.Provider>
</div>
);
};

const IncrementButton = React.memo(() => {
const incrementHandler = useContext(MyContext);

return (
<p>
<button onClick={incrementHandler}>+</button>
</p>
);
});


こぎ䞋では、MyContext.ProviderãĢæ¸Ąã™é–ĸ数をuseCallbackをį”¨ã„ãĻãƒĄãƒĸ化しãĻいぞす。įŦŦ 2 åŧ•æ•°ãŒ[]ということは、一åēĻ初期化された垌はupdateCountは毎回同一ぎé–ĸ数ã‚ĒブジェクトとãĒりぞす。よãŖãĻ、MyContext.Providerぎ値は変化しãĻいãĒã„æ‰ąã„ã¨ãĒり、IncrementButtonは再描į”ģされãĒくãĒりぞす。

ただし、IncrementButtonがReact.memoã§å›˛ã†ã‚ˆã†ãĢ変更されãĻいるį‚šãĢæŗ¨æ„ã—ãĻください。これはフックではãĒいぎでčŠŗį´°ã¯įœããžã™ãŒã€čĻĒぎUseCallbackSampleが再描į”ģされãĻもč‡Ē動įš„ãĢIncrementButtonが再描į”ģしãĒいようãĢする劚果がありぞす。こぎ 2 つぎæ–Ŋį­–ãĢよりIncrementButtonが再描į”ģされるčĻå› ãŒį„ĄããĒりぞす。こぎようãĢuseCallbackīŧˆã‚„useMemoīŧ‰ã¯æœ€éŠåŒ–ãĢ刊į”¨ã™ã‚‹ã“とができぞす。


useRef

useRefは、ref ã‚ĒブジェクトをäŊœãŖãĻčŋ”すフックです。こぎ ref ã‚ĒブジェクトはReact.createRefをäŊŋãŖãĻäŊœã‚‹ã“とができるã‚Ēブジェクトぎことです。useRefは、同じå‘ŧãŗå‡ēしãĢ寞しãĻは同じ ref ã‚Ēブジェクトをčŋ”しぞす。ref ã‚Ēブジェクトはcurrentプロパテã‚ŖãĢそぎ中čēĢがå…ĨãŖãĻいぞす。currentぎ初期値はuseRefぎåŧ•æ•°ã§æŒ‡åŽšã™ã‚‹ã“とができぞす。

useRefãŽã˛ã¨ã¤ãŽį”¨é€”は、ã‚ŗãƒŗポãƒŧネãƒŗトぎrefåąžæ€§ãĢæ¸Ąã™ãŸã‚ãŽrefã‚ĒブジェクトをäŊœã‚‹ã“とです。useEffectとįĩ„ãŋ合わせた䞋をäŊœãŖãĻãŋぞした。


useRefぎã‚ĩãƒŗプãƒĢ

import * as React from 'react';

const { useEffect, useRef } = React;

export const UseRefSample = () => {
const displayAreaRef = useRef();

useEffect(() => {
let rafid = null;
const loop = () => {
// įžåœ¨æ™‚åˆģã‚’čĄ¨į¤ē
const now = new Date();
displayAreaRef.current.textContent = `${String(now.getHours()).padStart(
2,
'0',
)}:${String(now.getMinutes()).padStart(2, '0')}:${String(
now.getSeconds(),
).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
rafid = requestAnimationFrame(loop);
};
loop();
return () => cancelAnimationFrame(rafid);
});

return <p ref={displayAreaRef} />;
};


こぎã‚ŗãƒŗポãƒŧネãƒŗトはこぎようãĒ襨į¤ēとãĒりぞす。

Image from Gyazo

これは、requestAnimationFrameをį”¨ã„ãĻãƒĒã‚ĸãƒĢã‚ŋイムでįžåœ¨æ™‚åˆģをミãƒĒį§’単äŊã§čĄ¨į¤ēするã‚ŗãƒŗポãƒŧネãƒŗトです。こぎようãĒéĢ˜é ģåēĻãĒ更新を゚テãƒŧトをį”¨ã„ãĻčĄŒã†ãŽã¯č˛ čˇãŒéĢ˜ãã†ãĒ気がするぎでuseEffectをį”¨ã„ãĻį›´æŽĨ DOM 操äŊœã‚’čĄŒãŖãĻいぞす。

į›´æŽĨ DOM 操äŊœã‚’čĄŒã†ãĢは、ãƒŦãƒŗダãƒĒãƒŗグ垌ãĢį”ŸãŽ DOM ノãƒŧドを取垗する斚æŗ•ãŒã‚りぞす。そぎ斚æŗ•ãŒã‚ŗãƒŗポãƒŧネãƒŗトぎrefåąžæ€§īŧˆ<p ref={displayAreaRef} />īŧ‰ã§ã™ã€‚こぎrefåąžæ€§ãĢæ¸Ąã™ãŸã‚ãŽ ref ã‚ĒブジェクトをuseRef()フックでäŊœãŖãĻいぞす。refåąžæ€§ãŽåŠšæžœãĢより、ãƒŦãƒŗダãƒĒãƒŗグ時ãĢ ref ã‚Ēブジェクトぎcurrentプロパテã‚ŖãĢ DOM ノãƒŧドがã‚ģットされぞす。こぎ䞋ではuseEffectぎハãƒŗãƒ‰ãƒŠãŽä¸­ã‹ã‚‰ã“ãŽæƒ…å ąã‚’äŊŋį”¨ã—ãĻいぞす。


ãƒŦãƒŗダãƒĒãƒŗグ間変数としãĻ useRef をäŊŋį”¨ã™ã‚‹

åž“æĨ、ref ã‚Ēãƒ–ã‚¸ã‚§ã‚¯ãƒˆã¨ã„ã†ãŽã¯å°‚ã‚‰ã€ä¸Šč¨˜ãŽäž‹ãŽã‚ˆã†ãĢ DOM ã‚Ēブジェクトへぎ参į…§ã‚’垗るぎãĢäŊŋわれãĻきぞした。しかし、フックぎ世į•ŒãĢおいãĻはuseRefや ref ã‚Ēブジェクトはそぎäžŋ刊さをさらãĢåĸ—しãĻいぞす。

ポイãƒŗトは、useRefがčŋ”すã‚ĒブジェクトはãƒŦãƒŗダãƒĒãƒŗグ間で毎回同じであるということです。į‰šãĢuseEffectをäŊŋį”¨ã™ã‚‹å ´åˆãĢ、ãƒŦãƒŗダãƒĒãƒŗã‚°é–“ã§æƒ…å ąã‚’å…ąæœ‰ã™ã‚‹å ´åˆãĢäŊŋį”¨å¯čƒŊです。

䞋えば、componentDidUpdateは「前回ぎ props」と「前回ぎ state」を参į…§å¯čƒŊでしたがuseEffectãĢはそぎ抟čƒŊはありぞせんでした。前回ぎ props ãĒおを刊į”¨ã—ãŸã„å ´åˆã¯ã€ããŽæƒ…å ąã‚’č‡Ē分でuseRefãĢäŋå­˜ã—ãĻおくことãĢãĒりぞす。

äģĨ下ぎ䞋は、č‡Ē分が再ãƒŦãƒŗダãƒĒãƒŗグされた回数をčĻšãˆãĻいるã‚ŗãƒŗポãƒŧネãƒŗトです。īŧˆæœŦåŊ“はこういうį”¨é€”でuseEffectをäŊŋわずãĢロジックでäŊ•ã¨ã‹ã—たãģうがよいですが。というか再ãƒŦãƒŗダãƒĒãƒŗグされた回数がåŋ…čĻãĒぎãŖãĻおんãĒå ´éĸãĒぎでしょうか。īŧ‰

export const UseRefSample2 = () => {

const displayAreaRef = useRef();
const renderCountRef = useRef(0);

useEffect(() => {
renderCountRef.current++;
displayAreaRef.current.textContent = String(renderCountRef.current);
});

return (
<p>
こぎã‚ŗãƒŗポãƒŧネãƒŗトは
<b ref={displayAreaRef} />
回描į”ģされぞした。
</p>
);
};

これと同じもぎをクナ゚ã‚ŗãƒŗポãƒŧネãƒŗトで書くとこんãĒ感じです。useRefで垗た ref ã‚Ēブジェクトがおおよそã‚ŗãƒŗポãƒŧネãƒŗトぎīŧˆã‚šãƒ†ãƒŧトではãĒいīŧ‰ãƒ—ロパテã‚ŖãĢ寞åŋœã—ãĻいることが分かると思いぞす。それãĢしãĻも、やãŖãąã‚Šãƒ•ãƒƒã‚¯ã‚’į”¨ã„たé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトぎãģã†ãŒã‚ˇãƒŗプãƒĢでいいですねīŧˆå ´åˆãĢもよりぞすがīŧ‰ã€‚

export class UseRefSample2 extends React.Component {

constructor(props) {
super(props);
this.displayArea = React.createRef();
this.renderCount = 0;
}
render() {
return (
<p>
こぎã‚ŗãƒŗポãƒŧネãƒŗトは
<b ref={this.displayArea} />
回描į”ģされぞした。
</p>
);
}
_effect() {
this.renderCount++;
this.displayArea.current.textContent = String(this.renderCount);
}
componentDidMount() {
this._effect();
}
componentDidUpdate() {
this._effect();
}
}


useLayoutEffect

useLayoutEffectは useEffectぎäēœį¨Žã§ã™ã€‚åŸēæœŦįš„ãĒäŊŋい斚はuseEffectと同じですが、ã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれるã‚ŋイミãƒŗã‚°ãŒé•ã„ãžã™ã€‚å…ˇäŊ“įš„ãĢは、useEffectはãƒŦãƒŗダãƒĒãƒŗグぎįĩæžœãŒæį”ģされた垌ãĢå‘ŧãŗå‡ēされぞすが、useLayoutEffectはãƒŦãƒŗダãƒĒãƒŗグįĩæžœãŒDOMãĢ反映された垌描į”ģされる前ãĢå‘ŧãŗå‡ēされぞす。

ãƒŦãƒŗダãƒĒãƒŗグįĩæžœãŒæį”ģされる前ãĢã‚ŗãƒŧãƒĢバックぎå‡Ļį†ãŒčĩ°ã‚‹ã¨ã„うį‰šåž´ãŽãŸã‚ã€useLayoutEffectぎå‡Ļį†ã¯ãƒŦãƒŗダãƒĒãƒŗグをブロックし、ãƒŦãƒŗダãƒĒãƒŗグįĩæžœãŒãƒĻãƒŧã‚ļãƒŧãĢčĻ‹ãˆã‚‹ãŽãŒé…åģļされぞす。ですから、そぎåŋ…čĻãŒãĒい場合はuseEffectをäŊŋうずきであるとされãĻã„ãžã™ã€‚ãĄãĒãŋãĢ、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトぎcomponentDidMountやcomponentDidUpdateぎã‚ŋイミãƒŗグはこぎuseLayoutEffectと同じです。useEffectは、ãƒŦãƒŗダãƒĒãƒŗグをブロックしãĒくãĒãŖたというį‚šã§ã“ã‚Œã‚‰ãŽé€˛åŒ–åŊĸã§ã‚ã‚‹ã¨č¨€ãˆã‚‹ã§ã—ã‚‡ã†ã€‚

useLayoutEffectをäŊŋう䞋を挙げãĻãŋぞす。つい先ãģおぎuseRefぎ䞋を思いå‡ēしãĻください。


useRefぎ䞋īŧˆå†æŽ˛īŧ‰

export const UseRefSample2 = () => {

const displayAreaRef = useRef();
const renderCountRef = useRef(0);

useEffect(() => {
renderCountRef.current++;
displayAreaRef.current.textContent = String(renderCountRef.current);
});

return (
<p>
こぎã‚ŗãƒŗポãƒŧネãƒŗトは
<b ref={displayAreaRef} />
回描į”ģされぞした。
</p>
);
};


これぎčŋ”り値をčĻ‹ãĻãŋると、bčĻį´ ãŽä¸­čēĢは最初įŠēです。useEffectぎã‚ŗãƒŧãƒĢバックãĢよãŖãĻこぎ中čēĢが埋められぞすが、ここãĢå•éĄŒãŒã‚ã‚Šãžã™ã€‚useEffectぎã‚ŗãƒŧãƒĢバックはãƒŦãƒŗダãƒĒãƒŗグが描į”ģ垌ãĢå‘ŧãŗå‡ēされるということは、こぎ中čēĢがįŠēぎįŠļ態が一įžŦãƒĻãƒŧã‚ļãƒŧãĢčĻ‹ãˆãĻしぞうぎです。原際、下ぎį”ģ像ぎようãĒ描į”ģãŒčĄ¨į¤ēされãĻしぞいぞす。

Image from Gyazo

useLayoutEffectをäŊŋį”¨ã™ã‚‹ã‚ˆã†ãĢ書き換えることでこぎįžčąĄã‚’回éŋできぞす。


useLayoutEffectぎ䞋

import * as React from 'react';

const { useLayoutEffect, useRef } = React;

export const UseLayoutEffectSample = () => {
const displayAreaRef = useRef();
const renderCountRef = useRef(0);

useLayoutEffect(() => {
renderCountRef.current++;
displayAreaRef.current.textContent = String(renderCountRef.current);
});

return (
<p>
こぎã‚ŗãƒŗポãƒŧネãƒŗトは
<b ref={displayAreaRef} />
回描į”ģされぞした。
</p>
);
};



useDebugValue

これが恐らく一į•Ē新しいフックで、React Hooksを昔はčŋŊãŖãĻいたという斚はįŸĨらãĒいかもしれぞせん。そぎ名ぎ通り、これはデバッグãĢäŊŋãˆã‚‹ãƒ•ãƒƒã‚¯ã§ã™ã€‚å…ˇäŊ“įš„ãĢは、ã‚Ģã‚šã‚ŋãƒ ãƒ•ãƒƒã‚¯ãŽãƒ‡ãƒãƒƒã‚°æƒ…å ąã‚’Reactぎ開į™ēč€…ãƒ„ãƒŧãƒĢį”¨æ‹Ąåŧĩ抟čƒŊãĢ襨į¤ēさせることができぞす。

これは先ãģおぎ「ãƒŦãƒŗダãƒĒãƒŗグ回数をčĻšãˆãĻいるã‚ŗãƒŗポãƒŧネãƒŗト」をベãƒŧã‚šãĢした䞋です。


useDebugValueぎ䞋

import * as React from 'react';

const { useEffect, useRef, useDebugValue } = React;

const useRenderCount = () => {
const renderCountRef = useRef(0);

useDebugValue(
`こぎã‚ŗãƒŗポãƒŧネãƒŗトは${renderCountRef.current}回再描į”ģされぞした`,
);

useEffect(() => {
renderCountRef.current++;
});
};
export const UseDebugValueSample = () => {
useRenderCount();

return <p>こぎã‚ŗãƒŗポãƒŧネãƒŗトを開į™ēč€…ãƒ„ãƒŧãƒĢでčĻ‹ã‚‹ã¨å†æį”ģæ•°ãŒčĄ¨į¤ēされぞす</p>;
};


ここで厚įžŠã•ã‚ŒãĻいるuseRenderCounté–ĸ数はã‚Ģã‚šã‚ŋムフックです。ã‚Ģã‚šã‚ŋムフックãĢついãĻはį­†č€…ぎäģĨå‰ãŽč¨˜äē‹ãĢč­˛ã‚Šãžã™ãŒã€čĻã™ã‚‹ãĢ名前がuseで始ぞるただぎé–ĸ数です。晎通は上ぎ䞋ぎようãĢ、いくつかぎフックå‘ŧãŗå‡ēしをぞとめãĻé–ĸ数ãĢしたもぎがã‚Ģã‚šã‚ŋムフックです。ただぎé–ĸ数ということは、あるã‚ŗãƒŗポãƒŧネãƒŗトからuseRenderCountã‚Ģã‚šã‚ŋムフックをå‘ŧãŗå‡ēすぎは、そぎ中čēĢãĢ書かれãĻいるフックを全部å‘ŧãŗå‡ēすぎと同じです。äŊ•ã‚‚į‰šæŽŠãĒことはありぞせん。

ただ、useDebugValueフックはč‡Ē分がおぎã‚Ģã‚šã‚ŋムフックからå‘ŧãŗå‡ēされたぎか検įŸĨしぞす3。そしãĻ、寞åŋœã™ã‚‹ã‚Ģã‚šã‚ŋムフックぎæ¨ĒãĢæŒ‡åŽšã—ãŸãƒ‡ãƒãƒƒã‚°æƒ…å ąã‚’čĄ¨į¤ēしぞす。

原際、こぎUseDebugValueSampleをReactį”¨æ‹Ąåŧĩ抟čƒŊでčĻ‹ã‚‹ã¨ä¸‹ãŽį”ģ像ぎようãĢ襨į¤ēされãĻいぞす。useDebugValueフックで指厚した値がuseRenderCountã‚Ģã‚šã‚ŋãƒ ãƒ•ãƒƒã‚¯ãŽãƒ‡ãƒãƒƒã‚°æƒ…å ąã¨ã—ãĻ襨į¤ēされãĻいることが分かりぞす。

Image from Gyazo

こぎフックは、これからåĸ—えるであろう、ã‚Ģã‚šã‚ŋムフックをナイブナãƒĒで提䞛するようãĒ場合ãĢäžŋ刊かもしれぞせん。


ãƒ‡ãƒãƒƒã‚°æƒ…å ąãŽé…åģļ計įŽ—

useDebugValueぎå‘ŧãŗå‡ēしはただぎJavaScriptã‚ŗãƒŧドですから、フックぎå‡Ļį†æ™‚ãĢは晎通ãĢåŽŸčĄŒã•ã‚Œãžã™ã€‚ã¤ãžã‚Šã€é–‹į™ēč€…ãƒ„ãƒŧãƒĢをäŊŋį”¨ã—ãĻいãĒい一čˆŦãƒĻãƒŧã‚ļãƒŧがã‚ĸプãƒĒã‚’åŽŸčĄŒã—ãĻいる場合であãŖãĻもuseDebugValueぎå‘ŧãŗå‡ēしはīŧˆåŽŸéš›čĻ‹ã‚‰ã‚Œã‚‹ã“とはãĒいぎでį„Ąæ„å‘ŗですがīŧ‰čĄŒã‚ã‚ŒãĻいぞす。

凝ãŖãŸãƒ‡ãƒãƒƒã‚°æƒ…å ąã‚’čĄ¨į¤ēしたい場合、useDebugValueぎåŧ•æ•°ãŽč¨ˆįŽ—が多少重いå‡Ļį†ãĢãĒるかもしれぞせん。そぎようãĒ場合、開į™ēč€…ãƒ„ãƒŧãƒĢをäŊŋわãĒいãƒĻãƒŧã‚ļãƒŧぎå‡Ļį†ãŒé‡ããĒãŖãĻしぞうぎは望ぞしくありぞせん。そぎようãĒäē‹æ…‹ã‚’éŋけるためãĢ、useDebugValueぎįŦŦ2åŧ•æ•°ãĢ、į”ŸãŽãƒ‡ãƒŧã‚ŋã‚’ãƒ‡ãƒãƒƒã‚°æƒ…å ąãĢ加åˇĨするé–ĸæ•°ã‚’æ¸Ąã™ã“ã¨ãŒã§ããžã™ã€‚ã“ã†ã™ã‚‹ã¨ã€ããŽé–ĸ数は原際ãĢãƒ‡ãƒãƒƒã‚°æƒ…å ąã‚’čĄ¨į¤ēする際ãĢåŽŸčĄŒã•ã‚Œãžã™ã€‚ã“ã‚ŒãĢã‚ˆã‚Šã€ãƒ‡ãƒãƒƒã‚°æƒ…å ąãŒåŋ…čĻãĒい場合ãĢäŊ™č¨ˆãĒ計įŽ—ã‚’įœãã“とができるというわけです。こぎ抟čƒŊはäģĨ下ぎようãĢäŊŋいぞす。

  useDebugValue(

renderCountRef.current,
count => `こぎã‚ŗãƒŗポãƒŧネãƒŗトは${count}回再描į”ģされぞした`,
);

こぎ䞋では、įŦŦ1åŧ•æ•°ã¯ã‚Ģã‚Ļãƒŗト数というį”ŸãŽãƒ‡ãƒŧã‚ŋãĢãĒり、それを文字列へと加åˇĨするå‡Ļį†ã¯é…åģļされるようãĢãĒりぞした。


useImperativeHandle

これが最垌ぎフックです。こぎフックは、ã‚ŗãƒŗポãƒŧネãƒŗトぎイãƒŗã‚šã‚ŋãƒŗã‚šãŒæŒã¤ãƒĄã‚Ŋッドをį”Ÿæˆã™ã‚‹ã“とができるフックです。äŊŋい斚は䞋を参į…§ã—ãĻください。ãĒお、ã‚ŗãƒŗポãƒŧネãƒŗトぎイãƒŗã‚šã‚ŋãƒŗ゚というぎは、refで取垗できるã‚Ēブジェクトぎことです。<div ref={myRef} />ぎようãĢDOMčĻį´ ãĢ寞しãĻrefをäŊŋãŖた場合はį”ŸãŽDOMノãƒŧドが垗られぞすが、<MyComponent ref={myRef} />ぎ場合はMyComponentã‚ŗãƒŗポãƒŧネãƒŗトぎイãƒŗã‚šã‚ŋãƒŗã‚šīŧˆãŽã‚ˆã†ãĒもぎīŧ‰ãŒåž—られることãĢãĒりぞす。MyComponent側がuseImperativeHandleフックをäŊŋうことで、ここで垗られるイãƒŗã‚šã‚ŋãƒŗã‚šãĢãƒĄã‚Ŋッドをį”Ÿã‚„すことができぞす。

æŦĄãŽäž‹ã¯ã€å°‘し前ãĢå‡ēãĻきた、įžåœ¨æ™‚åˆģをãƒĒã‚ĸãƒĢã‚ŋイムãĢ襨į¤ēするã‚ĩãƒŗプãƒĢを攚造したもぎです。


useImperativeHandleぎ䞋

import * as React from 'react';

const {
useEffect,
useRef,
useCallback,
useImperativeHandle,
forwardRef,
} = React;

export const UseImperativeHandleSample = () => {
const clockRef = useRef();

// ã‚šã‚ŋãƒŧトボã‚ŋãƒŗをæŠŧしたときぎå‡Ļį†
const onStart = useCallback(() => {
clockRef.current.start();
}, []);
// ゚トップボã‚ŋãƒŗをæŠŧしたときぎå‡Ļį†
const onStop = useCallback(() => {
clockRef.current.stop();
}, []);

return (
<div>
<Clock ref={clockRef} />
<p>
<button onClick={onStart}>再開</button>
<button onClick={onStop}>停æ­ĸ</button>
</p>
</div>
);
};

const Clock = forwardRef((_props, ref) => {
// 時åˆģã‚’čĄ¨į¤ēする場所ぎref
const displayAreaRef = useRef();
// ãƒĒã‚ĸãƒĢã‚ŋã‚¤ãƒ čĄ¨į¤ēがã‚Ēãƒŗかおうかぎref
const enabledFlagRef = useRef(true);

// ãƒĒã‚ĸãƒĢã‚ŋイムãĢ時åˆģã‚’čĄ¨į¤ēするå‡Ļį†
useEffect(() => {
let rafid = null;
const loop = () => {
// ãƒĒã‚ĸãƒĢã‚ŋã‚¤ãƒ čĄ¨į¤ēがã‚Ēãƒŗぎときぎãŋ襨į¤ēを更新
if (enabledFlagRef.current) {
// įžåœ¨æ™‚åˆģã‚’čĄ¨į¤ē
const now = new Date();
displayAreaRef.current.textContent = `${String(now.getHours()).padStart(
2,
'0',
)}:${String(now.getMinutes()).padStart(2, '0')}:${String(
now.getSeconds(),
).padStart(2, '0')}.${String(now.getMilliseconds()).padStart(3, '0')}`;
}
rafid = requestAnimationFrame(loop);
};
loop();
return () => cancelAnimationFrame(rafid);
});
// ã‚ŗãƒŗポãƒŧネãƒŗトぎイãƒŗã‚šã‚ŋãƒŗã‚šãŒæŒã¤ãƒĄã‚ŊッドをåŽŖ言
useImperativeHandle(ref, () => ({
start() {
enabledFlagRef.current = true;
},
stop() {
enabledFlagRef.current = false;
},
}));

return <p ref={displayAreaRef} />;
});


最垌ãĒãŽã§äž‹ãŒå°‘ã—é•ˇããĒãŖãĻいぞす。下で厚įžŠã•ã‚ŒãĻいるClockã‚ŗãƒŗポãƒŧネãƒŗトがuseImperativeHandleフックをäŊŋį”¨ã—ãĻstartとstopという2ã¤ãŽãƒĄã‚ŊッドをåŽŖč¨€ã—ãĻいぞす。こぎã‚ŗãƒŗポãƒŧネãƒŗトはenabledFlagRefぎ値をį”¨ã„ãĻ襨į¤ēを更新するかおうかをåˆļåžĄã—ãĻおり、こぎ2ã¤ãŽãƒĄã‚Ŋッドをį”¨ã„ãĻそれを外部からåˆļåžĄã§ãã‚‹ã‚ˆã†ãĢã—ã‚ˆã†ã¨ã„ã†é­‚čƒ†ã§ã™ã€‚äŊŋう側であるUseImperativeHandleSampleã‚ŗãƒŗポãƒŧネãƒŗトは、clockRefãĢClockã‚ŗãƒŗポãƒŧネãƒŗトぎイãƒŗã‚šã‚ŋãƒŗã‚šã‚’å…ĨれãĻ、ボã‚ŋãƒŗがæŠŧされるとそぎstartやstopをå‘ŧãŗå‡ēすようãĢãĒãŖãĻいぞす。

useImperativeHandleをäŊŋうときぎ最初ぎポイãƒŗトは、よくčĻ‹ã‚‹ã¨ClockがforwardRefというé–ĸ数ãĢ包ぞれãĻいるį‚šã§ã™ã€‚forwardRefは大雑把ãĢč¨€ãˆã°é–ĸ数型ã‚ŗãƒŗポãƒŧネãƒŗトぎrefを加åˇĨしたいときãĢäŊŋうもぎです。äģŠå›žã¯ãžã•ãĢuseImperativeHandleãĢよãŖãĻrefãĢãƒĄã‚Ŋッドをį”Ÿã‚„そうとしãĻいるぎでした。

forwardRefぎåŧ•æ•°ã¨ã—ãĻé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗãƒˆã‚’æ¸Ąã™ãŽã§ã™ãŒã€įŦŦ2åŧ•æ•°ã¨ã—ãĻrefãŒæ¸Ąã•ã‚Œã‚‹ã‚ˆã†ãĢãĒãŖãĻいぞす。これがそぎぞぞuseImperativeHandleぎįŦŦ1åŧ•æ•°ã¨ãĒりぞす。įŦŦ2åŧ•æ•°ã¯ã‚Ēブジェクトをčŋ”すé–ĸ数です。čŋ”されたã‚Ēブジェクトが持ãŖãĻã„ã‚‹ãƒĄã‚Ŋッドが、そぎぞぞこぎã‚ŗãƒŗポãƒŧネãƒŗトぎrefãŽãƒĄã‚ŊッドとãĒりぞす。


名前ãĢついãĻ

Imperativeというぎは「手įļšãįš„」ということです。手įļšãįš„というぎは、propsį­‰ãĢよãŖãĻåŽŖ言įš„ãĢUIを厚įžŠã™ã‚‹Reactぎæĩå„€ã‹ã‚‰å¤–ã‚ŒãĻいることを意å‘ŗしãĻいぞす。原際、こぎ䞋だãŖãĻuseImperativeHandleをäŊŋうåŋ…į„ļ性があるわけではãĒく、ã‚Ēãƒŗ/ã‚Ēフぎフナグをpropsã§æ¸Ąã™ã“ã¨ã‚‚å¯čƒŊです。そもそもã‚ŗãƒŗポãƒŧネãƒŗトãĢãƒĄã‚Ŋッドをį”Ÿã‚„すというぎはã‚ŗãƒŗポãƒŧネãƒŗトをpropsīŧˆã‚„ã‚ŗãƒŗテキ゚トīŧ‰äģĨ外ぎ手æŽĩでåˆļåžĄã—ã‚ˆã†ã¨ã„ã†ã“ã¨ã§ã™ã‹ã‚‰ã€įœŸãŖ向からReactぎやり斚ãĢ反しãĻいるぎがお分かりãĢãĒると思いぞす。それでも需čĻãŒã‚るからこそこぎフックがčŋŊ加されたぎでしょうが、あぞりįŠæĨĩįš„ãĢäŊŋうもぎでもãĒã„ã‚ˆã¨ã„ã†ãƒĄãƒƒã‚ģãƒŧジが名前ãĢčĄ¨ã‚ŒãĻいぞす。


ぞとめ

äģĨ上で、React Hooksぎ最初ぎæ­Ŗåŧį‰ˆãŒå°Žå…ĨされたReact 16.8ãĢ存在するフックを全ãĻč§ŖčĒŦしぞした。個äēēįš„ãĢよくäŊŋいそうãĒぎはuseStateとuseEffect、あとuseRefあたりです。useRefはuseEffectãŽãƒ­ã‚¸ãƒƒã‚¯ãŒč¤‡é›‘åŒ–ã—ãĻきたらå‡ēį•Ēがåĸ—えãĻきぞす。副äŊœį”¨ã‚’č¤‡é›‘åŒ–ã•ã›ã‚‹ãŽã¯ã‚ãžã‚Šč¤’ã‚ã‚‰ã‚ŒãŸã“ã¨ã§ã¯ã‚ã‚Šãžã›ã‚“ãŒã€‚

途中äŊ•å›žã‹ã‚¯ãƒŠã‚šã‚ŗãƒŗポãƒŧネãƒŗトとぎ比čŧƒã‚’挟ãŋぞしたが、さすが垌į™ēぎAPIだけあãŖãĻã€ã‚ˆã‚Šã‚ˇãƒŗプãƒĢでį›´æ„Ÿįš„ãĒ記čŋ°ãŒå¯čƒŊãĢãĒãŖãĻいるぎがお分かりãĢãĒãŖたことでしょう。そもそもé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトč‡ĒäŊ“ãŒã‚ˇãƒŗプãƒĢãĒAPIということもあり、ã‚Ŋãƒŧã‚šã‚ŗãƒŧãƒ‰ãŽã‚ˇãƒŗプãƒĢさãĢ大きくč˛ĸįŒŽã—ãĻくれぞす。ぞた、最初é–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトで書いãĻいたぎãĢįŠļ態がåŋ…čĻãĢãĒãŖãĻしぞãŖたときãĢ、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトãĢ書きį›´ã™åŋ…čĻãŒãĒいというぎもåŦ‰ã—いį‚šã§ã™4。

途中äŊ•å›žã‹ãƒĒãƒŗクしぞしたが、į­†č€…ぎäģ–ãŽč¨˜äē‹ãĢã‚Ģã‚šã‚ŋãƒ ãƒ•ãƒƒã‚¯ã‚’å–ã‚Šæ‰ąãŖたもぎがありぞす。äģŠå›žį´šäģ‹ã—ãŸãƒ•ãƒƒã‚¯ãŸãĄã‚’įĩ„ãŋ合わせãĻã‚Ģã‚šã‚ŋムフックをäŊœã‚‹ã“とこそReact HooksぎæœŦčŗĒã§ã‚ã‚‹ã¨č¨€ãŖãĻã‚‚éŽč¨€ã§ã¯ã‚ã‚Šãžã›ã‚“ã€‚ãã†ã€ã“ã‚Œã‚’čĒ­ãŋįĩ‚わãŖãĻãĒるãģおと思ãŖたあãĒたはぞだReact Hooksぎ゚ã‚ŋãƒŧトナイãƒŗぎ3ãƒĄãƒŧトãƒĢくらい手前ãĢいるįŠļ態ãĒãŽã§ã™ã€‚ãœã˛ã“ãĄã‚‰ãŽč¨˜äē‹ã‚‚čĒ­ã‚“でReact Hooksぎ゚ã‚ŋãƒŧトを切ãŖãĻください。





  1. 一åŋœčŖœčļŗしãĻおくと、クナ゚ã‚ŗãƒŗポãƒŧネãƒŗトãĒおぎ旧æĨぎ抟čƒŊが React Hooks ãĢ取ãŖãĻäģŖわられãĻåģƒæ­ĸされるというäēˆåŽšã¯äģŠãŽã¨ã“ろありぞせん。ですから、React Hooks をéŋけãĒがら React をäŊŋいįļšã‘ることも可čƒŊです。į­†č€…はそういうäēēīŧˆ/チãƒŧムīŧ‰ã¯ React をäŊŋうぎãĢ向いãĻいãĒいというčĒŦを推しぞすが。 ↊



  2. įŦŦ 2 åŧ•æ•°ãŽįœį•Ĩと、įŦŦ 2 åŧ•æ•°ãĢ[]を指厚するぎとはį•°ãĒるというį‚šãĢæŗ¨æ„ã—ãĻください。įŦŦ 2 åŧ•æ•°ã‚’įœį•Ĩした場合はãƒŦãƒŗダãƒĒãƒŗグごとãĢã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれぞすが、[]を指厚した場合は初回ぎãƒŦãƒŗダãƒĒãƒŗグでぎãŋã‚ŗãƒŧãƒĢバックé–ĸ数がå‘ŧばれぞす。 ↊



  3. ãĄã‚ƒã‚“ã¨čĒŋずたわけではありぞせんが、おうやらErrorã‚Ēブジェクトをį”Ÿæˆã—ãĻã‚ŗãƒŧãƒĢã‚šã‚ŋックをå…Ĩ手しãĻいるようです。 ↊



  4. recomposeをäŊŋãŖãĻいるäēēはé–ĸ数ã‚ŗãƒŗポãƒŧネãƒŗトぎぞぞでもいけるぞと思ãŖたかもしれぞせん。それはある意å‘ŗでæ­Ŗしく、React HooksはrecomposeãŽé€˛åŒ–įŗģã¨č€ƒãˆã‚‹ã“ã¨ã‚‚ã§ããžã™ã€‚ãĒお、recomposeはReact Hooksぎį™ģ場と同時ãĢ抟čƒŊčŋŊ加į­‰ãŽåœæ­ĸがåŽŖč¨€ã•ã‚Œãžã—ãŸã€‚äģŠåžŒã¯React HooksがrecomposeãĢ取ãŖãĻäģŖわることãĢãĒりぞす。 ↊