iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0
Modern Web

React 走出新手村 系列 第 17

React 走出新手村 — 在組件裡犯的錯(I)

  • 分享至 

  • xImage
  •  

因果關係

這次來分享 code review 時常常會看到的問題,這些問題常常看不出來有什麼影響,甚至有的人會覺得那是 coding style 的範疇,尤其是在台灣開發的基本準則 — 能動就好,大環境影響下而忽略其本身就是錯誤的問題,下面就來分享一下這些範例吧。

天生反骨的寫法

來還原一下情境,這個傢伙叫小帥,小帥本來是一個 Java 的開發者,突然公司要他經手一個項目,前端的部分必須使用 React 來開發,因為客戶想省錢希望有機會能移植到 React-Native 上,出雙平台的 App 產品,小帥心想:『之前已經有其他框架的開發經驗了,前端都差不多啦!還不都一樣,客戶的標準 — 能動就好,不會太難啦,文件就是有錯再邊看邊改就好。』

下面來簡化一下他發生的情境:

import { useState } from "react";

export default function App() {
  // 不知道為什麼不拆,可能覺得好管理
  // txtInput也不知道為什麼不大寫->TxtInput
  // 也許他就是將jsx當成一般的function了吧
  const txtInput = () => {
    const [val, setVal] = useState("");
    const txtChange = (e) => setVal(e.target.value);
    return (
      <>
        <input value={val} onChange={txtChange} />
      </>
    );
  };
  return (
    <div className="App">
    {/* 不知道不能小寫,然後會error,接著💩就出現了 */}
    {/* <txtInput /> /*}
      {txtInput()}
    </div>
  );
}

這樣的修改看起來能動,如果是新版本的 React 會跳提醒說 jsx 使用方式的錯誤(依照小帥的調性選擇先忽略),但是加入其他的 component 的時候就會發生很嚴重的後果,我們試著加入一個開關來隱藏 txtInput,用來模擬其他組件在連動時的錯誤:

import { useState } from "react";

export default function App() {
  // 模擬情境,大家可以自行腦補成更複雜的結構
  const [show, setShow] = useState(false);
  const toggle = () => setShow(!show);
  const txtInput = () => {
    const [val, setVal] = useState("");
    const txtChange = (e) => setVal(e.target.value);
    return (
      <>
        <input value={val} onChange={txtChange} />
      </>
    );
  };
  return (
    <div className="App">
    <button onClick={toggle}>show</button>
    {/* 帶入show讓他隱藏 */}
      {show && txtInput()}
    </div>
  );
}

來看看實際畫面與錯誤:
https://ithelp.ithome.com.tw/upload/images/20230915/20129020AnjfKi2o0G.png
按下按鈕後
https://ithelp.ithome.com.tw/upload/images/20230915/20129020h6ikybbxsN.png

那麼正確來說應該怎麼做呢?

首先我們來看兩者差異,先讓我們調整回原本default的版本,然後來個別查看其中的差異。

import { useState } from "react";
// 改拉到外面,這個概念在之前的文章也有分享過了
const TxtInput = () => {
  const [val, setVal] = useState("");
  const txtChange = (e) => setVal(e.target.value);
  return (
    <>
      <input value={val} onChange={txtChange} />
    </>
  );
};
export default function App() {
  
  return (
    <div className="App">
      {/* 這裡請大家依照情況,個別註解掉下面的code並去查看devtool */}
      <TxtInput />
      {TxtInput()}
    </div>
  );
}

如果我們先註解掉下面的 💩 code,那麼我們可以透過devtool看到如下圖:
https://ithelp.ithome.com.tw/upload/images/20230915/20129020BTO5tCsXJx.png
https://ithelp.ithome.com.tw/upload/images/20230915/201290200RtPIuqitg.png

都很正常,App 裡面有 TxtInput,然後 TxtInput 裡面有使用 state 管理 inputvalue & onChange 事件。

那麼,我們再來查看💩 code 的部分,把現在的寫法註解掉並解除 💩 code 的封印:
https://ithelp.ithome.com.tw/upload/images/20230915/20129020c8LAFRIbXy.png

有沒有發現 App 裡面什麼也沒包住,原本的 TxtInput 反而跑到 hooks 裡面了,包了一個寂寞。

那麼要怎麼修改勒?

讓我們把原本的寫法換回Jsx的寫法就能夠正常了,如下:

import { useState } from "react";
// 這裡移出來不影響,就單純看了覺得啊砸
const TxtInput = () => {
  const [val, setVal] = useState("");
  const txtChange = (e) => setVal(e.target.value);
  return (
    <>
      <input value={val} onChange={txtChange} />
    </>
  );
};

export default function App() {
  const [show, setShow] = useState(false);
  const toggle = () => setShow(!show);
  
  return (
    <div className="App">
    <button onClick={toggle}>show</button>
    {/* 將原本的TxtInput()改為<TxtInput/> */}
      {show && <TxtInput/>}
    </div>
  );
}

那麼再回到畫面去確認 devtool,應該會如下:
https://ithelp.ithome.com.tw/upload/images/20230915/20129020ZmyJhT5R0W.png
https://ithelp.ithome.com.tw/upload/images/20230915/20129020loNsr5OL3J.png
https://ithelp.ithome.com.tw/upload/images/20230915/20129020EHvWeD7Eht.png

你可能會想問:為什麼第一個方法才可以,後面直接運行就不行?

以下為chatGPT給出解釋的重點:

JSX 通過編譯過程將其轉換為普通的 JavaScript 代碼,以便瀏覽器可以理解和執行。這樣可以使開發人員在構建用戶界面時更加方便和高效。同時,React 提供了對 JSX 的原生支持,並提供了相應的函數和工具來處理 JSX 語法。

所以,當你直接以 javascript 方式運行的話就會不產生 jsx 的封包 function — React.createElement(),所以當你的程式經過包版工具、babel轉譯後,它被當作是一般的 js 給輸出了。

當然你也可以寫成以下:

import { useState } from "react";
// 這裡移出來不影響,就單純看了覺得啊砸
const TxtInput = () => {
  const [val, setVal] = useState("");
  const txtChange = (e) => setVal(e.target.value);
  return (
    <>
      <input value={val} onChange={txtChange} />
    </>
  );
};

export default function App() {
  const [show, setShow] = useState(false);
  const toggle = () => setShow(!show);
  
  return (
    <div className="App">
    <button onClick={toggle}>show</button>
    {/* 將原本的<TxtInput/>改成以下 */}
      {show && React.createElement(TxtInput, null)}
    </div>
  );
}

這樣也是可以正常運行的,當然我相信聰明的你會選擇簡單的。
https://ithelp.ithome.com.tw/upload/images/20230915/20129020RvC7ffL4B8.png

結論

『能動就好』是台灣開發環境的通病,要想改善這樣的思維,其實都要靠大家凝聚相同的共識。

提問環節也很重要,走出新手村的重要觀念就是要去反思,為什麼看似理所當然的事情,背後到底處理了哪些東西?

用與不用的差異,有實驗精神多踩坑再爬起來,會比較容易理解背後的思想,常說寫程式是思維邏輯的推倒,比起抱怨和比較哪個框架好用,不如多多理解所用的工具,有哪些細節是你沒注意到的。

給全新手的大禮包

React基本Hook教學


上一篇
React 走出新手村 — 樣式的選擇
下一篇
React 走出新手村 — 在組件裡犯的錯(II)
系列文
React 走出新手村 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言