ItIron2023
react
昨天我們再次探討了一些基本的useState觀念以及react的一些render邏輯,基本上你只要知道第一天強調的兩個造成re-render的情況,多數類似的實務問題你都會比較有方向切入,事情會遠沒有你想得這麼複雜,剩下的就是一些觀念的綜合應用! 希望前兩天有讓你建立一點信心,那麼今天我們稍微加點料,讓另一個hook近來湊個熱鬧,也讓問題變得更實務一些吧!
馬上就開始吧,請觀察這個codesandbox內的程式碼。
這是一個相當常見的情況,你利用fecth或是axios去向後端發送請求取得某些資料後再利用setState保存資料,最終渲染出你所要的畫面。 在這個例子中我們向jsonplaceholder發出請求,已知api endpoint本身並沒有任何問題,但畫面卻沒有順利渲染。
並報出了TypeError:Cannot read properties of undefined (reading 'name')。
請觀察以下的程式碼並試著修復此問題。
export default function App() {
const [user, setUser] = useState();
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((data) => setUser(data))
.catch((error) => console.error("Error fetching data:", error));
}, []);
return (
<div className="App">
<h1>
Uncaught TypeError: Cannot set properties of undefined with useState & useEffect
</h1>
<div>The user name is {user.name}</div>
<div>The user age is {user.phone}</div>
</div>
);
}
這次的問題就稍微有意思一些了,甚至我在實務中也看過幾個工程師或學習者犯過類似的錯誤。實際上這個問題並不完全算是useState或是useEffect在搞事,而是react本身的render機制以及一個非常單純的js問題。
首先你必須先了解useEffect執行時機,它會在第一次render後被呼叫,但很遺憾的是在這次的範例中,第一次render時就已經出現TypeError導致程式中斷,因此若你在hook裡面加入簡單的log或是觀察network,你會發現useEffect裡面的callback從未被執行
useEffect(() => {
console.log("I called") // 這行永遠不會被印出
fetch("https://jsonplaceholder.typicode.com/users/1")
.then((response) => response.json())
.then((data) => setUser(data))
.catch((error) => console.error("Error fetching data:", error));
}, []);
至於為什麼第一次的render會失敗呢? 這就跟state以及render function內的寫法有關了,注意看一下我們的setState method
const [user, setUser] = useState();
在沒有初始值的情況下user實際上會是undefined, 而你試圖去讀取undefined的任何屬性自然會出現TypeError,了解根本原因後要處理就簡單多囉,這個題目你有數種方法可以處理,我這邊列出比較常見的寫法
1. Conditional rendering
在render前先確認user object,有值我們再印
{user && (
<>
<div>The user name is {user.name}</div>
<div>The user age is {user.phone}</div>
</>
)}
2. Optional chainging
想要facny一點的話也可以用新的語法,實際上效果會有點類似,不過畫面呈現效果會有些不同,使用者的相關資訊會突然閃出來。
<div>The user name is {user?.name}</div>
<div>The user age is {user?.phone}</div>
3. Proper initial value
第三種算是最直觀的做法了,你知道問題核心在於你試圖去取undefined的值,那麼就給一個簡單的初始值就行了,比方說一個空物件,這樣的做法不管是在邏輯或是畫面上都與方法2相當類似。
const [user, setUser] = useState({});
當然,若你今天已經知道物件的詳細schema,那麼你也可以用個類似DEFAULT_USER之類的通用變數去處理這個問題。
// initialize and import DEFAULT_USER from some other files
const DEFAULT_USER = {name: '', phone: ''}
const [user, setUser] = useState(DEFAULT_USER);
今天我們配合了useState & useEffect來示範一個常見的問題,透過說明以及幾個解法你應該對於useEffect的執行和react render機制有著更多一些些的理解,文章內帶過的幾個觀念都會在後續的問題不斷地用到,希望這樣一步步的帶領能幫助你有效的吸收這些觀念!我們明天見囉!
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!