iT邦幫忙

2023 iThome 鐵人賽

DAY 3
0
Modern Web

30天React練功坊-攻克常見實務/面試問題系列 第 3

30天React練功坊-攻克常見實務/面試問題 Day3: Uncaught TypeError: Cannot set properties of undefined

  • 分享至 

  • xImage
  •  
tags: ItIron2023 react

前言

昨天我們再次探討了一些基本的useState觀念以及react的一些render邏輯,基本上你只要知道第一天強調的兩個造成re-render的情況,多數類似的實務問題你都會比較有方向切入,事情會遠沒有你想得這麼複雜,剩下的就是一些觀念的綜合應用! 希望前兩天有讓你建立一點信心,那麼今天我們稍微加點料,讓另一個hook近來湊個熱鬧,也讓問題變得更實務一些吧!

本日題目

馬上就開始吧,請觀察這個codesandbox內的程式碼。

day3-demo-image

這是一個相當常見的情況,你利用fecth或是axios去向後端發送請求取得某些資料後再利用setState保存資料,最終渲染出你所要的畫面。 在這個例子中我們向jsonplaceholder發出請求,已知api endpoint本身並沒有任何問題,但畫面卻沒有順利渲染。

day3-api-request

並報出了TypeError:Cannot read properties of undefined (reading 'name')。

day3-demo-image-error

請觀察以下的程式碼並試著修復此問題。

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機制有著更多一些些的理解,文章內帶過的幾個觀念都會在後續的問題不斷地用到,希望這樣一步步的帶領能幫助你有效的吸收這些觀念!我們明天見囉!

本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!


上一篇
30天React練功坊-攻克常見實務/面試問題 Day2: setState isn't working correctly when called multiple times
下一篇
30天React練功坊-攻克常見實務/面試問題 Day4: Way too many state in a component
系列文
30天React練功坊-攻克常見實務/面試問題30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言