如何应用 SOLID 原则整理 React 代码之单一原则

简介: SOLID 原则的主要是作为关心自己工作的软件专业人员的指导方针,那些以经得起时间考验的设计精美的代码库为荣的人。

640 (5).png

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])
    // 复杂UI渲染
    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 调用的加载状态。


为了更好地理解这个例子,我把它简化了。但是你几乎可以在任何地方的同一个组件中找到它们!这里发生了很多事情:


  1. 远程数据的获取


  1. 数据过滤


  1. 复杂的状态管理


  1. 复杂的 UI 功能


因此,让我们探索如何改进代码的设计并使其紧凑。


1. 移动数据处理逻辑


不要将 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>
        })}
    </>
}


看看我们的组件现在是多么的小,多么的容易理解!这是在错综复杂的代码库中所能做的最简单、最重要的事情。


但我们可以做得更好。


2. 可重用的数据获取钩子


现在,当我们看到我们 useGetRemoteData Hook 时,我们看到这个 Hook 正在做两件事:


  1. 从远程数据源获取数据


  1. 过滤数据


让我们把获取远程数据的逻辑提取到一个单独的钩子,这个钩子的名字是

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}
}


干净多了,对吧? 我们能做得更好吗? 当然,为什么不呢?


3. 分解 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 的强大功能创建漂亮的可重用组件。


本文为译文,原文链接:https://betterprogramming.pub/how-to-apply-solid-principles-to-clean-your-code-in-react-cdfd5e0a9cea[1]


参考资料


[1]

https://betterprogramming.pub/how-to-apply-solid-principles-to-clean-your-code-in-react-cdfd5e0a9cea:https://betterprogramming.pub/how-to-apply-solid-principles-to-clean-your-code-in-react-cdfd5e0a9cea


目录
相关文章
|
1月前
|
资源调度 前端开发 Android开发
如何在 React Native 中进行代码签名验证?
如何在 React Native 中进行代码签名验证?
|
2月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
1月前
|
监控 JavaScript 前端开发
如何在实际应用中测试和比较React和Vue的性能?
总之,通过多种方法的综合运用,可以相对客观地比较 React 和 Vue 在实际应用中的性能表现,为项目的选择和优化提供有力的依据。
36 1
|
1月前
|
前端开发 JavaScript 开发者
使用React和Redux构建高效的前端应用
使用React和Redux构建高效的前端应用
36 1
|
1月前
|
监控 前端开发 JavaScript
确保 React 应用在出现错误时仍然能够保持响应式
【10月更文挑战第25天】可以有效地确保React应用在出现错误时仍然能够保持响应式,为用户提供更加稳定、可靠的使用体验。在实际应用中,需要根据项目的具体情况和需求,综合运用这些方法,并不断优化和完善错误处理机制,以适应不断变化的业务场景和用户需求
|
2月前
|
前端开发 JavaScript 测试技术
React 高阶组件 (HOC) 应用
【10月更文挑战第16天】高阶组件(HOC)是 React 中一种复用组件逻辑的方式,通过接受一个组件并返回新组件来实现。本文介绍了 HOC 的基础概念、核心功能和常见问题,包括静态方法丢失、ref 丢失、多个 HOC 组合和 props 冲突的解决方案,并提供了具体的 React 代码示例。通过本文,读者可以更好地理解和应用 HOC,提高代码的复用性和可维护性。
87 8
|
2月前
|
JavaScript 前端开发
使用 React 和 Redux 构建动态图表应用
【10月更文挑战第3天】使用 React 和 Redux 构建动态图表应用
|
2月前
|
JavaScript 前端开发
使用 React 和 Redux 构建一个计数器应用
【10月更文挑战第3天】使用 React 和 Redux 构建一个计数器应用
|
3月前
|
前端开发 JavaScript 区块链
react18函数组件+antd使用指南-使用代码集合以及报错记录汇总
本文介绍了多个React开发中常见的问题及其解决方案,包括但不限于:1)`useForm`实例未连接到任何`Form`元素的警告及解决方法;2)监听页面滚动事件的实现方式;3)React 18与antd 5.8.6中定制主题的方法;4)React结合antd 4.x版本自定义主题色的步骤;5)解决`ResizeObserver loop`相关报错的技巧;6)处理React设计表单时遇到的CDN资源加载失败问题;7)解决onClick事件传参问题;8)修复类型错误等。每部分均提供详细分析与实用代码示例,帮助开发者快速定位并解决问题。
69 2
|
2月前
|
设计模式 SQL 前端开发
使用 GraphQL 和 React 构建动态前端应用
【10月更文挑战第2天】使用 GraphQL 和 React 构建动态前端应用
65 0