提升你的 React 開發效率 透過三個目標重構元件撰寫方式

me
林彥成
2021-03-12 | 3 min.
文章目錄
  1. 1. 重構元件撰寫方式的三個目標
    1. 1.1. 更少的打字時間
    2. 1.2. 更少的除錯與測試時間
    3. 1.3. 更少的閱讀時間
  2. 2. 重構元件範例

重構元件撰寫方式的三個目標

這篇文章會分享三個加速 React.js 開發的目標,主要內容皆從底下這篇文章翻譯而來

  1. 更少的打字時間
  2. 更少的除錯與測試時間
  3. 更少的閱讀時間

Double your react coding speed with this simple trick

更少的打字時間

要讓打字時間縮短,大致上分為兩個方向:

  1. DRY 增加共用的程式碼: 大元件拆分成小且可重用的 hooks 或元件

    • 資料處理邏輯獨立
    • 元件只處理顯示
    • 簡化輸入參數 (props)
  2. 善用工具

    • Snippets: 編輯器外掛協助自動完成
    • Auto Import: 編輯器外掛協助路徑輸入
    • prettier: 編輯器自動格式化

更少的除錯與測試時間

通常找到錯誤的流程要先把 App 用開發模式跑起來後,操作元件重現錯誤並查看相關錯誤訊息,修復後重試。在開發階段如果要避免低階錯誤,可分成兩個方向

  1. 撰寫測試並導入 CI/CD,在流程中就靠機器提早幫我們發現問題
  2. 善用工具,在開發階段透過 eslint、props type check、TypeScript 來避免低階問題

更少的閱讀時間

開發者最常花時間在看懂程式碼而非撰寫,通常好的程式都有容易被修改、容易發現問題的優點,那在撰寫上列出幾個我覺得需要改掉的壞習慣:

  • 程式碼太多暗號
  • 類似功能卻散在各處
  • 太長的函式,需要花時間才能了解
  • 使用上需要滿滿的參數卻又未分類
  • 太多布林值狀態
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 程式碼太多暗號
value[0][index] = sum;

// 滿滿的參數卻又未分類
<Grid
data={gridData}
pagination={false}
autoSize={true}
enableSort={true}
sortOrder="desc"
disableSelection={true}
infiniteScroll={true}
...
/>

const options = {
pagination: false,
autoSize: true,
enableSort: true,
sortOrder: 'desc',
disableSelection: true,
infiniteScroll: true,
...
}

<Grid
data={gridData}
options={options}
/>

// 太多布林值狀態

const [isLoading, setIsLoading] = useState(false)
const [isFinished, setIsFinished] = useState(false)
const [hasError, setHasError] = useState(false)

const fetchSomething = () => {
setIsLoading(true)

fetch(url)
.then(() => {
setIsLoading(false)
setIsFinished(true)
})
.catch(() => {
setHasError(true)
})
}

const [state, setState] = useState('idle')

const fetchSomething = () => {
setState('loading')

fetch(url)
.then(() => {
setState('finished')
})
.catch(() => {
setState('error')
})
}

重構元件範例

元件優化的方向與過程,最主要還是拆解成可重複使用的小單位,像是運用 render props 或是 HOC 甚至是寫成 Custom hook,對岸維護的 ahook 功能就非常多樣。常見可以優化的部分我覺得分以下三個部分:

  1. 打 API 的寫法
    • 初階: 在元件裡面抓資料
    • 進階: 透過 redux 這類工具統一資料處理邏輯
    • 進階: 寫一個 hook 或是使用 react-query
  2. 錯誤處理
    • 初階: 直接在打 API 的 function 中寫判斷
    • 進階: ErrorBoundary 處理或是 axios interceptor
  3. 樣式檔的優化
    • 初階: CSS
    • 進階: Sass or SCSS
    • 進階: CSS-in-JS
    • 進階: Atomic-css

最開始的元件撰寫通常會長成下面這樣,在同樣一個元件裡面放了狀態,打 API 抓資料的相關程式,還有顯示的部分,好處當然就是直觀也不會說不好維護,但當這樣的元件有 50 個的時候,打 API 的方式要修改時,也許就會懷疑人生?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import React, { useEffect, useState } from "react";

import AddModal from "../components/AddModal";
import LoadingIndicator from "../components/LoadingIndicator";
import BrowserItem from "../components/BrowserItem";

import colors from "../config/colors";

function Browsers() {
const URL = "https://google.com/myData.json";

const [loading, setLoading] = useState(true);
const [browsers, setBrowsers] = useState([]);

const [modalVisible, setModalVisible] = useState(false);
const [description, setDescription] = useState("");

const changeDescription = (description) => {
setDescription(description);
setModalVisible(!modalVisible);
};

const changeOpacity = () => {
setModalVisible(!modalVisible);
};

useEffect(() => {
fetch(URL)
.then((response) => response.json())
.then((responseJson) => {
return responseJson.Browsers;
})
.then((browsers) => {
setBrowsers(browsers);
// console.log(browsers)
setLoading(false);
})
.catch((error) => {
console.log(error);
})
.finally(() => setLoading(false));
}, []);

return (
<>
{loading ? (
<LoadingIndicator />
) : (
<>
<AddModal
modalVisible={modalVisible}
changeOpacity={() => changeOpacity()}
description={description}
/>
<List
data={browsers}
keyExtractor={(browser) => browser.fullname}
renderItem={({ item }) => (
<BrowserItem
{...item}
changeDescription={() => changeDescription(item.description)}
/>
)}
/>
</>
)}
</>
);
}

export default Browsers;

將資料邏輯切割成更小可以重複使用的 hooks,當這些資料存取寫法被共用時就達到

  • 加速開發: 因為重複使用
  • 更快速找到問題: 遇到問題可以減少閱讀和測試這些部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { useEffect, useState } from "react";

function useFetch(url) {
const [loading, setLoading] = useState(false);
const [data, setData] = useState(undefined);

useEffect(() => {
setLoading(true);
fetch(url)
.then((response) => response.json())
.then(setData)
.finally(() => setLoading(false))
.catch((error) => Alert.alert("Fetch error", error));
}, [url]);

return {
loading,
data,
};
}

function useBrowsers(url) {
const { loading, data } = useFetch(url);
const [selectedBrowser, setSelectedBrowser] = useState(undefined);

return {
loading,
browsers: data?.Browsers,
selectedBrowser,
setSelectedBrowser,
};
}

切割成更小可重用的元件且拿掉大部分的資料邏輯,僅留下 conditional render 的相關實作,元件行數下降找問題的速度肯定又更上層樓。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function UIFriendlyList(props) {
if (props.loading) {
return <LoadingIndicator />;
}

if (props?.data && props.data.length === 0) {
return <Text>This list is empty (</Text>;
}

return <List {...props} />;
}

// BrowsersList.tsx
function BrowsersList(props) {
const { loading, selectedBrowser, setSelectedBrowser, browsers } = props;
return (
<View style={styles.container}>
<AddModal
modalVisible={Boolean(selectedBrowser)}
onClose={() => setSelectedBrowser(undefined)}
description={selectedBrowser?.description}
/>
<UIFriendlyList
loading={loading}
data={browsers}
renderItem={({ item }) => (
<BrowserItem
key={item.fullname}
browser={item}
onPress={() => setSelectedBrowser(item)}
/>
)}
/>
</View>
);
}

function Browsers() {
return <BrowsersList {...useBrowsers("https://google.com/myData.json")} />;
}

喜歡這篇文章,請幫忙拍拍手喔 🤣

share