這次來分享 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>
);
}
來看看實際畫面與錯誤:
按下按鈕後
那麼正確來說應該怎麼做呢?
首先我們來看兩者差異,先讓我們調整回原本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看到如下圖:
都很正常,App 裡面有 TxtInput
,然後 TxtInput
裡面有使用 state
管理 input
的 value
& onChange
事件。
那麼,我們再來查看💩 code 的部分,把現在的寫法註解掉並解除 💩 code 的封印:
有沒有發現 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,應該會如下:
你可能會想問:為什麼第一個方法才可以,後面直接運行就不行?
以下為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>
);
}
這樣也是可以正常運行的,當然我相信聰明的你會選擇簡單的。
『能動就好』是台灣開發環境的通病,要想改善這樣的思維,其實都要靠大家凝聚相同的共識。
提問環節也很重要,走出新手村的重要觀念就是要去反思,為什麼看似理所當然的事情,背後到底處理了哪些東西?
用與不用的差異,有實驗精神多踩坑再爬起來,會比較容易理解背後的思想,常說寫程式是思維邏輯的推倒,比起抱怨和比較哪個框架好用,不如多多理解所用的工具,有哪些細節是你沒注意到的。