reactとreduxを一気に学習すると最初はわかったつもりでも
忘れたり、こんがらがったりすることがありました。
その中での備忘録です。
getElementByIdでオブジェクトが取れない
自分の場合はコンポーネントのライフサイクルを完全に忘れてしまっていました。
コンポーネントのライフサイクルをざっくり書くと
コンポーネントがマウントされて
↓
再レンダーされて
↓
アンマウントされる
というものです。
const ButtonText = () => {
const buttonName = document.getElementById("button").innerText;
const getButtonName = () => {
alert(buttonName)
}
return (
<section>
<button onClick={() => {getButtonName()}} id="button">ボタン</button>
</section>
)
}
getElementByIdの1行はreturnの前に実行されてしまうので例外が走って
TypeError: document.getElementById(...) is null
となってしまいます。
こうなるとreturnの後、つまりコンポーネントライフサイクルでいうと
マウントされた後に実行したいとなるわけですが
そこで最近であればuseEffect※1を使う場面となります。
※1react hooksの関数
const ButtonText = () => {
let buttonName = ""; //1
const getButtonName = (buttonName) => {
alert(buttonName) //4 クリックされた時点で実行。クリックされなければ実行されない
}
useEffect((event) => {
buttonName = document.getElementById("button").innerText; //3
},[]);
return ( //2
<section>
<button onClick={() => {getButtonName(buttonName)}} id="button">ボタン</button>
</section>
)
}
useEffectの詳細は割愛させていただきますが
上記のようにuseEffectのコールバック関数で実行することで
コンポーネントマウント後にgetElementByIdが実行されるので
オブジェクトが取得できるようになります。
コメントアウトの番号は実行される順番になります。
無限ループが起こる
関数でコンポーネントを定義していてreturnの中でクリックイベントを発生させたい時。
具体的には以下のような形で書くと無限ループが発生するようになりました。
const CounterComponent = () => {
let [count, counter] = useState(0);
const countUp = () => {
counter(count + 1);
}
return (
<section>
<button onClick={countUp}>カウント|{count}</button>
</section>
)
}
export default CounterComponent
以下エラーメッセージです。
Too many re-renders. React limits the number of renders to prevent an infinite loop.
ここでポイントとなるのが前述ソース内の
onClick={countUp}
この書き方だとcountUp関数はクリックしなくても最初コンポーネントマウント時に実行されてしまうんですね。
するとsetStateする関数であるcounterが走る
そうなるとstateが変化したので再レンダー処理が走る(上記ソース内でのreturn文)
つまり
countUp
↓
counter(stateがセットされ再レンダーの合図)
↓
return(再レンダー)
↓
countUp
・
・
・
となりめでたく無限ループが完成します。
そこで対策として
onClick={() => {countUp}}
として即時関数でラップしてあげると
クリックされて初めてcountUpが実行されます。
所感
学習時は理屈がわかって、写経して動いても
実際自分で組み立てて使う時にハマり
わかってないところが浮き彫りになりますね。
1つ1つ丁寧に理解することも復習時には大事だと実感しました。