如果您是一名开发人员,那么您肯定会在您的编程生涯中多次听过 SOLID 原则这个词。在软件开发中,SOLID 原则可作为开发人员的指导方针。无论您在项目中使用哪种语言,为了使您的代码干净且可维护,您需要在您的项目中应用 SOLID 原则。
SOLID 原则使开发人员更容易完成任务,也有助于他们维护项目中的代码。现在让我们谈谈 React,这是一个非常受开发人员欢迎的框架。
在 React的帮助下,您可以创建漂亮的 UI。在你职业生涯的早期阶段,你可能会在 React 中编写代码时犯很多错误,但是一旦你有了相关的工作经验,你就会明白在 React 中编写干净且可维护的代码也很重要。出于这个原因,肯定可以帮助您的一件事是 SOLID 原则。
您可以编写小巧、美观且干净的 React 组件。 SOLID 原则使您的组件可见且职责明确。 SOLID 原则告诉我们每个类都应该有一个存在的目的。在 React 中,组件一次只能做一件事。
现在让我们了解如何在 React 中重构糟糕的代码并使其更清晰。首先,让我们考虑一个不好的例子……
Javascript
import React, {useEffect, useReducer, useState} from "react";
const initialState = {
isLoading: true
};
// COMPLEX STATE MANAGEMENT
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)
}
// REMOTE DATA FETCHING
useEffect(() => {
dispatch({type:'LOADING'})
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(json => {
dispatch({type:'FINISHED'})
setUsers(json)
})
},[])
// PROCESSING DATA
useEffect(() => {
const filteredUsers = users.map(user => {
return {
id: user.id,
name: user.name,
contact: `${user.phone} , ${user.email}`
};
});
setFilteredUsers(filteredUsers)
},[users])
// COMPLEX UI RENDERING
return <>
Users List
Loading state: {state.isLoading? 'Loading': 'Success'}
{users.map(user => {
return showDetails(user.id)}>
{user.name}
{user.email}
})}
>
}
Javascript
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}
}
Javascript
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 <>
Users List
Loading state: {isLoading? 'Loading': 'Success'}
{filteredUsers.map(user => {
return showDetails(user.id)}>
{user.name}
{user.email}
})}
>
}
Javascript
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}
}
Javascript
export function loadingReducer(state, action) {
switch (action.type) {
case 'LOADING':
return {isLoading: true};
case 'FINISHED':
return {isLoading: false};
default:
return state;
}
}
Javascript
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}
}
Javascript
const UserDetails = (user) => {
const showDetails = (user) => {
alert(user.contact)
}
return showDetails(user)}>
{user.name}
{user.email}
}
Javascript
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const Users = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
return <>
Users List
Loading state: {isLoading? 'Loading': 'Success'}
{filteredUsers.map(user => )}
>
}
在这里,我们从远程源获取数据,然后在 UI 中呈现它。我们也在检测 API 调用的加载状态。基本上,上面的代码主要分为……四件事……
- 远程数据获取…
- 数据过滤…
- 复杂的状态管理…
- 复杂的用户界面功能…
现在让我们看看如何改进此代码的设计以及如何使其更易于清理……
1. 从代码中分离数据处理逻辑。
您永远不应该将 HTTP 调用保留在组件内。这是一个基本的经验法则。要从组件中删除这些代码,您可以遵循多种策略。
您可以创建自定义挂钩,并且可以将数据获取和过滤逻辑移动到该自定义挂钩中。让我们看看如何做到这一点……
创建一个名为 useGetRemoteData 的挂钩。看起来像下面…
Javascript
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}
}
现在,如果您查看主要组件,它将如下所示……
Javascript
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 <>
Users List
Loading state: {isLoading? 'Loading': 'Success'}
{filteredUsers.map(user => {
return showDetails(user.id)}>
{user.name}
{user.email}
})}
>
}
您可以观察到您的组件现在更加清晰和易于理解。让我们使用更多的技术或方法使我们的代码更好。
2. 分离数据获取的代码,使其可重用
useGetRemoteData 在您的代码中有两个用途……
- 从远程源获取数据
- 过滤数据
我们可以制作一个单独的钩子,我们可以将我们的数据获取逻辑移到那里。让我们给它起个名字……useHttpGetRequest。它将 URL 作为一个组件。
Javascript
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 逻辑分离到一个单独的文件中……
Javascript
export function loadingReducer(state, action) {
switch (action.type) {
case 'LOADING':
return {isLoading: true};
case 'FINISHED':
return {isLoading: false};
default:
return state;
}
}
执行以上两个操作后……useGetRemoteData 如下图……
Javascript
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}
}
现在您可以观察到代码变得更加清晰。我们可以执行更多的操作,让这段代码变得更好。让我们看看如何做到这一点……
3. 分解 UI 组件
将用户详细信息的代码分离到不同的组件中,该组件仅负责显示用户详细信息。
Javascript
const UserDetails = (user) => {
const showDetails = (user) => {
alert(user.contact)
}
return showDetails(user)}>
{user.name}
{user.email}
}
现在原始组件如下所示:
Javascript
import React from "react";
import {useGetRemoteData} from "./useGetRemoteData";
export const Users = () => {
const {filteredUsers , isLoading} = useGetRemoteData()
return <>
Users List
Loading state: {isLoading? 'Loading': 'Success'}
{filteredUsers.map(user => )}
>
}
你有没有观察到你的代码太长了,现在太短了?我们只是将代码分解为五个独立的组件,然后将我们的逻辑放在那里。每个组件现在负责单一职责。
让我们回顾一下我们的代码,看看我们在这里做了什么。我们创建了五个不同的组件……
- Users.js:负责展示用户列表。
- UserDetails.js:负责显示用户的详细信息
- useGetRemoteData.js:负责过滤远程数据
- useHttpGetrequest.js:负责HTTP调用
- LoadingReducer.js:复杂的状态管理。
希望你现在清楚了。