如何應用 SOLID 原則整理 React 代碼之單一原則
本文爲譯文,原文鏈接:betterprogramming.pub/how-to-appl…
推薦語:該文章對於剛學習 React 的同學來說還是非常有幫助的,通過運用單一原則可以把組件劃分、邏輯劃分的代碼進行整理
SOLID 原則的主要是作爲關心自己工作的軟件專業人員的指導方針,另外還爲那些以經得起時間考驗的設計精美的代碼庫爲榮的人。
今天,我們將從一個糟糕的代碼示例開始,應用 SOLID 的第一個原則,看看它如何幫助我們編寫小巧、漂亮、乾淨的並明確責任的 React 組件,。
什麼是單一責任原則?
單一責任原則告訴我們的是,每個類或組件應該有一個單一的存在目的。
組件應該只做一件事,並且做得很好。
讓我們重構一段糟糕但正常工作的代碼,並使用這個原則使其更加清晰和完善。
讓我們從一個糟糕的例子開始
首先讓我們看看一些違反這一原則的代碼,添加註釋是爲了更好地理解:
import React, {useEffect, useReducer, useState} from "react";
const initialState = {
isLoading: true
};
function reducer(state, action) {
switch (action.type) {
case 'LOADING':
return {isLoading: true};
case 'FINISHED':
return {isLoading: false};
default:
return state;
}
}
export const SingleResponsibilityPrinciple = () => {
const [users , setUsers] = useState([])
const [filteredUsers , setFilteredUsers] = useState([])
const [state, dispatch] = useReducer(reducer, initialState);
const showDetails = (userId) => {
const user = filteredUsers.find(user => user.id===userId);
alert(user.contact)
}
useEffect(() => {
dispatch({type:'LOADING'})
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => {
dispatch({type:'FINISHED'})
setUsers(json)
})
},[])
useEffect(() => {
const filteredUsers = users.map(user => {
return {
id: user.id,
name: user.name,
contact: `${user.phone} , ${user.email}`
};
});
setFilteredUsers(filteredUsers)
},[users])
return <>
<div> Users List</div>
<div> Loading state: {state.isLoading? 'Loading': 'Success'}</div>
{users.map(user => {
return <div key={user.id} onClick={() => showDetails(user.id)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
</>
}
這段代碼的作用
這是一個函數式組件,我們從遠程數據源獲取數據,再過濾數據,然後在 UI 中顯示數據。我們還檢測 API 調用的加載狀態。
爲了更好地理解這個例子,我把它簡化了。但是你幾乎可以在任何地方的同一個組件中找到它們!這裏發生了很多事情:
-
遠程數據的獲取
-
數據過濾
-
複雜的狀態管理
-
複雜的 UI 功能
因此,讓我們探索如何改進代碼的設計並使其緊湊。
- 移動數據處理邏輯
不要將 HTTP 調用保留在組件中。這是經驗之談。您可以採用幾種策略從組件中刪除這些代碼。
您至少應該創建一個自定義 Hook 並將數據獲取邏輯移動到那裏。例如,我們可以創建一個名爲 useGetRemoteData
的 Hook,如下所示:
import {useEffect, useReducer, useState} from "react";
const initialState = {
isLoading: true
};
function reducer(state, action) {
switch (action.type) {
case 'LOADING':
return {isLoading: true};
case 'FINISHED':
return {isLoading: false};
default:
return state;
}
}
export const useGetRemoteData = (url) => {
const [users , setUsers] = useState([])
const [state, dispatch] = useReducer(reducer, initialState);
const [filteredUsers , setFilteredUsers] = useState([])
useEffect(() => {
dispatch({type:'LOADING'})
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => {
dispatch({type:'FINISHED'})
setUsers(json)
})
},[])
useEffect(() => {
const filteredUsers = users.map(user => {
return {
id: user.id,
name: user.name,
contact: `${user.phone} , ${user.email}`
};
});
setFilteredUsers(filteredUsers)
},[users])
return {filteredUsers , isLoading: state.isLoading}
}
現在我們的主要組件看起來像這樣:
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const SingleResponsibilityPrinciple = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
const showDetails = (userId) => {
const user = filteredUsers.find(user => user.id===userId);
alert(user.contact)
}
return <>
<div> Users List</div>
<div> Loading state: {isLoading? 'Loading': 'Success'}</div>
{filteredUsers.map(user => {
return <div key={user.id} onClick={() => showDetails(user.id)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
})}
</>
}
看看我們的組件現在是多麼的小,多麼的容易理解!這是在錯綜複雜的代碼庫中所能做的最簡單、最重要的事情。
但我們可以做得更好。
- 可重用的數據獲取鉤子
現在,當我們看到我們 useGetRemoteData
Hook 時,我們看到這個 Hook 正在做兩件事:
-
從遠程數據源獲取數據
-
過濾數據
讓我們把獲取遠程數據的邏輯提取到一個單獨的鉤子,這個鉤子的名字是 useHttpGetRequest
,它把 URL 作爲一個參數:
import {useEffect, useReducer, useState} from "react";
import {loadingReducer} from "./LoadingReducer";
const initialState = {
isLoading: true
};
export const useHttpGetRequest = (URL) => {
const [users , setUsers] = useState([])
const [state, dispatch] = useReducer(loadingReducer, initialState);
useEffect(() => {
dispatch({type:'LOADING'})
fetch(URL)
.then(response => response.json())
.then(json => {
dispatch({type:'FINISHED'})
setUsers(json)
})
},[])
return {users , isLoading: state.isLoading}
}
我們還將 reducer 邏輯移除到一個單獨的文件中:
export function loadingReducer(state, action) {
switch (action.type) {
case 'LOADING':
return {isLoading: true};
case 'FINISHED':
return {isLoading: false};
default:
return state;
}
}
所以現在我們的 useGetRemoteData
變成了:
import {useEffect, useState} from "react";
import {useHttpGetRequest} from "./useHttpGet";
const REMOTE_URL = 'https://jsonplaceholder.typicode.com/users'
export const useGetRemoteData = () => {
const {users , isLoading} = useHttpGetRequest(REMOTE_URL)
const [filteredUsers , setFilteredUsers] = useState([])
useEffect(() => {
const filteredUsers = users.map(user => {
return {
id: user.id,
name: user.name,
contact: `${user.phone} , ${user.email}`
};
});
setFilteredUsers(filteredUsers)
},[users])
return {filteredUsers , isLoading}
}
乾淨多了,對吧? 我們能做得更好嗎? 當然,爲什麼不呢?
- 分解 UI 組件
看看我們的組件,其中顯示了用戶的詳細信息。我們可以爲此創建一個可重用的 UserDetails 組件:
const UserDetails = (user) => {
const showDetails = (user) => {
alert(user.contact)
}
return <div key={user.id} onClick={() => showDetails(user)}>
<div>{user.name}</div>
<div>{user.email}</div>
</div>
}
最後,我們的原始組件變成:
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const Users = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
return <>
<div> Users List</div>
<div> Loading state: {isLoading? 'Loading': 'Success'}</div>
{filteredUsers.map(user => <UserDetails user={user}/>)}
</>
}
我們把代碼從 60 行精簡到了 12 行!我們創建了五個獨立的組成部分,每個部分都有明確而單一的職責。
讓我們回顧一下我們剛剛做了什麼
讓我們回顧一下我們的組件,看看我們是否實現了 SRP:
-
Users.js
- 負責顯示用戶列表 -
UserDetails.js
ー 負責顯示用戶的詳細資料 -
useGetRemoteData.js
- 負責過濾遠程數據 -
useHttpGetrequest.js
- 負責 HTTP 調用 -
LoadingReducer.js
- 複雜的狀態管理
當然,我們可以改進很多其他的東西,但是這應該是一個很好的起點。
總結
這是一個簡單的演示,演示如何減少每個文件中的代碼量,並使用 SOLID 的強大功能創建漂亮的可重用組件。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://juejin.cn/post/6963480203637030926