ItIron2023
react
總算又到了今年的鐵人賽了,今年的主題其實讓我掙扎很久,原先我是打算寫一些前端AI工具使用或是一些框架的深度技術文,但一直以來我都是希望多幫助一些處於入門階級的初學者,畢竟這是我自己認為比較需要幫助的階段,不管是協助他們打好基礎或是面試練習等都有得到不錯的回饋。因此我最終還是決定順從自己的心聲,把重點放在這方面的協助上,如果對過去的文章有興趣的話可以參考我前幾年的系列文,基本上都是為了新手工程師或學習者寫的文章!
每日挑戰,從Javascript面試題目了解一些你可能忽略的概念
Have fun! 新手也能打造的Javascript微型專案
這次的主題對我來說稍微比較特別一些,一直以來我都跟所有與我接觸學習者強調一個概念:『比起增加工具或框架的熟練度,請優先熟練javascript』,畢竟工具或框架總會被淘汰,但如果你本身基礎打得夠扎實,那我不怕你學不了新的東西。那為什麼我還是要寫這樣的主題呢?除了js面試題這個主題我已經寫過之外,主要有以下兩個原因
因此希望透過這一系列文章協助有這類需求的工程師,無論你是要做面試準備或是想透過簡單的實戰練習增加自己對react的理解,我想這次的系列文都會對你有幫助!不過也因為主題選擇的關係,建議你需要滿足以下的條件再來看這系列文章
1. 熟悉javascript語法與核心概念
2. 對於react & react hooks有著基本理解
另外,在這次的系列文中我會盡量用簡單的方式說明問題的根本原因與解決方案,許多較為細節的react底層實作原理我會簡短帶過或是忽略,講難聽點就是我只會讓你知道足夠解決該情境的背景知識,如果是想深究更底層的東西就需要自己去研究囉!好了,落落長的前言就到這邊!我們開始吧!
我們今天先從最最簡單的題目開始吧,請觀察這個codesandbox內的程式碼。
今天我們期望點擊按鈕後push一個Leo
人名到我們的DEMO_NAME_LIST
陣列,並透過setState method重新setNames,一切邏輯都很直接地寫在我們的handleClick function
const handleClick = () => {
names.push("Leo");
setNames(names);
console.log(names);
};
從console中你也可以看到確實在點擊後names
變數是有被更新的,但畫面卻沒有出現對應的更新。
我想對於有碰過react的工程師來說,這個題目再簡單也不過了,關鍵在於你是否了解react是怎麼去判斷是否需要重新渲染(re-render)組件,別擔心,我並不是要大談reconciliation & diffing algorithm,我們從基本的開始就行了。
大致上來說react會在以下兩種情況重新渲染組件
1. 組件本身的state或是收到的props更新(update)
2. 該組件的父組件(parent component) re-render
這次的例子只有單一組件,因此很明顯會是第一種情況觸發重新渲染,不過問題就來了,在上方的例子看起來state確實有被更新啊? 印出的log有看出names確實有變動,為什麼畫面還是沒有更新呢?
原因在於react對於更新的定義,判斷一個state或是props是否更新會採用Object.is()
去做比較,若結果為true則會跳過重新渲染的操作,因此對於物件來說,你務必要產生一個新的reference才能觸發重新渲染,題目中的push並不會建立一個新的陣列,而是對原有的陣列進行操作.也因此被react判斷說並未更新,自然就不會觸發重新渲染囉!
解決方法也相當的簡單,只要掌握剛剛說的原則,以下的寫法都是給過的!
const handleClick = () => {
// 善用callback存取前一次的state
setNames(preNames => [...preNames, 'Leo']);
// 或是單純建立一個新的陣列再做操作
const updatedNames = [...names];
updatedNames.push('Leo');
setNames(updatedNames)
};
透過今天的題目我們了解了react在什麼樣的情況下才會觸發重新渲染,當然我們省略了一些更為底層的細節,但今天學到的東西就足以應付這個常見問題了!下次當畫面沒有正確重新渲染時我想你就比較有概念該往哪個方向去思考囉!明天我們一樣會繼續做相關的實戰,敬請期待!
React 官方文件 https://react.dev/reference/react/useState#setstate-caveats
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!