6-4 專案程式碼重構

本單元對應的專案分支為:create-view-of-weather-card-for-refactoring

單元核心#

這個單元的主要目標包含:

  • 將程式碼進行 React 元件的拆檔與整理,以利後續開發維護

不知道你有沒有發現,雖著專案的功能越來越多,程式碼的量也越來越大,特別針對 App.js 這支檔案,你有沒有覺得內容好像多到找個想要改的東西時常常不容易找到呢?

一開始撰寫專案時,為了避免實作時要在不同檔案之間切來切去,容易造成混淆,所以就先把大多數的功能都寫在 App.js 中而沒有額外把獨立的函式或元件拆分開來,但這麼做當專案越來越大的時候,如果沒有善用元件拆分和 JavaScript 模組化,會開始變得越來越難維護。

info

有的開發者習慣先把多個功能寫在同一個元件中,待功能都開發完成後再來進行元件的拆分;有些開發者則習慣一開始就把不同畫面或功能的元件拆開來開發。筆者認為要先進行元件拆分,或待功能完成後再進行拆分,端看開發者個人習慣。

重構(refactoring)的意思通常是指在不改變原本功能的情況下,把程式碼改成用更容易維護、更容易理解、或更精簡的方式來改寫。現在可以先把和在 App 元件中,與 <WeatherCard> 有關的功能進行檔案的拆分。

在這個單元中我們就來進行專案程式碼的重構,透過元件的拆分之外,讓程式碼看起來更易讀與好維護吧!

建立 WeatherCard 元件#

先在 ./src 資料夾內新增名為 views 的資料夾,並在裡面建立一支名為 WeatherCard.js 的檔案,放入起手勢的程式碼:

// ./src/views/WeatherCard.js
import React from 'react';
const WeatherCard = () => {
return /* ... */;
};
export default WeatherCard;

通常和一整個頁面有關的內容會放在 views 的資料夾,現在雖然只有 WeatherCard 這個頁面,但後續我們還會實作設定頁面。

接著把原本放在 WeatherApp 內 <WeatherCard>...</WeatherCard> 的部分都複製到 WeatherCard.js 的元件中:

Imgur

需要稍微留意一下的是,原本在 App 元件中,WeatherCard 區塊最外層的名稱是 <WeatherCard> ,但現在因為元件的名稱就已經是 WeatherCard(const WeatherCard = () => {/*...*/}),因此在 WeatherCard.js 中,我們把原本最外層的 <WeatherCard> 改名為 <WeatherCardWrapper>

搬移和 WeatherCard 有關的變數與樣式#

接下來,把當初定義在 App.js 中和 WeatherCard 元件有關的 Styled Components 也一起搬進來 WeatherCard.js 中:

  1. 在 WeatherCard 元件中一樣要先匯入 emotion 和 days
// ./src/views/WeatherCard.js
// ...
import styled from '@emotion/styled';
import dayjs from 'dayjs';
  1. 在 App 元件中,除了最外層的 <Container> 之外,其他都是和 WeatherCard 有關的 styled components,全部搬進 WeatherCard.js 中。這裡要記得把原本名為 WeatherCard 的 styled components 改名為 WeatherCardWrapper
  2. 接著搬移有用到的元件和圖示,包含:
// ./src/views/WeatherCard.js
// ...
import WeatherIcon from './../components/WeatherIcon.js';
import { ReactComponent as AirFlowIcon } from './../images/airFlow.svg';
import { ReactComponent as RainIcon } from './../images/rain.svg';
import { ReactComponent as RefreshIcon } from './../images/refresh.svg';
import { ReactComponent as LoadingIcon } from './../images/loading.svg';
  1. 在 App 元件中匯入並使用 WeatherCard 元件:
import WeatherCard from './views/WeatherCard';
const App = () => {
// ...
return (
<ThemeProvider theme={theme[currentTheme]}>
<Container>
<WeatherCard />
</Container>
</ThemeProvider>
);
};
  1. 將 WeatherCard 需要的資料,透過 props 從 App 元件傳入,其中包含:
  • 資料:「天氣資料(weatherElement)」以及「白天或晚上(moment)」
  • 函式:因為在 WeatherCard 中的重新整理需要呼叫 fetchData 這個方法,所以也需要一併透過 props 傳入
// ./src/App.js
const App = () => {
// ...
return (
// ...
<WeatherCard
weatherElement={weatherElement}
moment={moment}
fetchData={fetchData}
/>
// ...
);
};
小提醒

透過 props 不只可以傳遞「字串」、「物件」、「陣列」、「數值」這類資料,也可以直接把「函式」傳進去。

  1. 接著在 WeatherCard 中取出傳入的 props,並以解構賦值的方式將變數取出:
// ./src/views/WeatherCard.js
const WeatherCard = ({ weatherElement, moment, fetchData }) => {
const {
observationTime,
locationName,
temperature,
windSpeed,
description,
weatherCode,
rainPossibility,
comfortability,
isLoading,
} = weatherElement;
// ...
};

到這一步的時候,總算搬移完成,透過 npm start 將專案啟動後,可以看到畫面又再次恢復正常了:

Imgur

移除 App 元件中多餘的程式碼#

最後在 App 元件中,因為很多變數都已經移到 WeatherCard 元件中,因此很多變數在 App 元件中是用不到的,如果你在 VSCode 有安裝 ESLint 這個套件的話,程式編輯器會透過底線或顏色來提示哪些變數是多餘的,可以依建議移除即可。

換你了!將 WeatherCard 獨立成一個元件#

透過重構和元件的拆分,這裡我們把拉取資料和處理資料的邏輯保留在 App 元件中,把資料的呈現搬移到了 WeatherCard 元件中,將邏輯處理和畫面呈現加以區隔開來。現在你可以參考下面的步驟來嘗試看看:

  • ./src/views 中建立 WeatherCard 元件
  • 在 WeatherCard 中匯入所需的第三方套件
  • 將 App 元件中 JSX 內的 <WeatherCard /> 區塊搬移到 WeatherCard 元件內
  • 搬移和 WeatherCard 有關的 Styled Components(在 App 元件中除了 <Container /> 外,其他的 styled components 都需搬移)
  • 搬移和 WeatherCard 有關的圖示
  • 把原本最外層的 <WeatherCard> 改名為 <WeatherCardWrapper>
  • 在 App 元件中匯入並使用 WeatherCard 元件
  • 透過 props 將 WeatherCard 所需的資料或方法由 App 元件中傳入,並於 WeatherCard 中取出使用
  • 移除 App 元件中多餘的程式碼

本單元相關之網頁連結、完整程式碼與程式碼變更部分可於 create-view-of-weather-card-for-refactoring 分支檢視:https://github.com/pjchender/learn-react-from-hook-realtime-weather-app/tree/create-view-of-weather-card-for-refactoring

Imgur