教你应用 SOLID 原则整理 React 代码之单一原则

目录
  • 什么是单一责任原则?
  • 让我们从一个糟糕的例子开始
  • 1. 移动数据处理逻辑
  • 2. 可重用的数据获取钩子
  • 3. 分解 UI 组件
  • 让我们回顾一下我们刚刚做了什么
  • 总结

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 调用的加载状态。

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

  • 远程数据的获取
  • 数据过滤
  • 复杂的状态管理
  • 复杂的 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 正在做两件事:

  • 从远程数据源获取数据
  • 过滤数据

让我们把获取远程数据的逻辑提取到一个单独的钩子,这个钩子的名字是 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 的强大功能创建漂亮的可重用组件。

到此这篇关于如何应用 SOLID 原则整理 React 代码之单一原则的文章就介绍到这了,更多相关React  SOLID单一原则内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 如何应用 SOLID 原则在 React 中整理代码之开闭原则

    目录 本系列其他文章 什么是开闭原则? 让我们从一个例子开始 一个糟糕的解决方案 解决方案是什么? 让我们创建单独的用户组件 注意 总结 SOLID 是一套原则.它们主要是关心代码质量和可维护性的软件专业人员的指导方针. React 不是面向对象,但这些原则背后的主要思想可能是有帮助的.在本文中,我将尝试演示如何应用这些原则来编写更好的代码. 在前一篇文章中,我们讨论了单一责任原则.今天,我们将讨论 SOLID 的第二个原则: 开闭原则. 本系列其他文章 如何应用 SOLID 原则在 React

  • 在React中应用SOLID原则的方法

    目录 1.单一职责原则(SRP) 2.开放封闭原则(OCP) 3.里氏替换原则(LSP) 4.接口隔离原则(ISP) 5.依赖倒置原则(DIP) 6.小结 在面向对象编程(OOP)中,SOLID 原则是设计模式的基础,它的每个字母代表一种设计原则: 单一职责原则(SRP) 开放封闭原则(OCP) 里氏替换原则(LSP) 接口隔离原则(ISP) 依赖倒置原则(DIP) 下面就来看看每个原则的含义以及如何在 React 中应用 SOLID 原则! 1.单一职责原则(SRP) 单一职责原则的定义是每个

  • 教你应用 SOLID 原则整理 React 代码之单一原则

    目录 什么是单一责任原则? 让我们从一个糟糕的例子开始 1. 移动数据处理逻辑 2. 可重用的数据获取钩子 3. 分解 UI 组件 让我们回顾一下我们刚刚做了什么 总结 SOLID 原则的主要是作为关心自己工作的软件专业人员的指导方针,那些以经得起时间考验的设计精美的代码库为荣的人. 今天,我们将从一个糟糕的代码示例开始,应用 SOLID 的第一个原则,看看它如何帮助我们编写小巧.漂亮.干净的并明确责任的 React 组件,. 什么是单一责任原则? 单一责任原则告诉我们的是,每个类或组件应该有一

  • 干货!教大家如何选择Vue和React

    两者之间的相同之处 同样是基于组件开发的轻量级框架,同样是专注于用户界面的视图view层. 如何选择 1.1 如果喜欢用模板搭建应用(或者有这个想法)选择Vue Vue应用默认的是把markup放在HTML中,数据绑定表达式和Angular一样,采用{{}}的形式,而指令(特殊的HTML属性)用来向模板中添加功能 <div> <p>{{ message }}</p> <button v-on:click="reverseMessage">

  • JS+FLASH幻灯片播放图片脚本,整理了代码,使得调用更加方便!

    JS+FLASH幻灯片播放图片脚本,整理了代码,使得调用更加方便很多大站上都有的JS+FLASH幻灯片播放图片脚本,整理了代码,使得调用更加方便! 生成幻灯片式的网页图片效果演示 生成幻灯片式的网页图片效果演示 // 幻灯片式的网页图片滚动函数(不需要做任何改动) // 本程序由广州房产网整理,欢迎转载及提意见,版权没有 function roll_pic_flash(roll_pic_ary) { // 生成幻灯片式的网页图片滚动代码 var w = roll_pic_ary[3][0]; v

  • 使用React代码动态生成栅格布局的方法

    作为 TerminalMACS 的一个子进程模块 - React Web管理端,使用Ant Design Pro作为框架. 本文应用到的知识1.样式文件less中方法的使用2.for循环创建按钮和栅格布局(flex布局) 1. 最终效果及源码链接 作为前端新手的我,做下面这个简单功能花了好几天时间,问了不少前端大佬(大佬们应该要不到半个小时,惭愧惭愧),现在回想问的问题都很基础(有点丢人,哈哈),多谢了哦. 先看看效果 点击浏览源码:开源项目对应源码 2. 代码简单讲解 2.1 TypeScri

  • 详解React 代码共享最佳实践方式

    任何一个项目发展到一定复杂性的时候,必然会面临逻辑复用的问题.在React中实现逻辑复用通常有以下几种方式:Mixin.高阶组件(HOC).修饰器(decorator).Render Props.Hook.本文主要就以上几种方式的优缺点作分析,帮助开发者针对业务场景作出更适合的方式. Mixin 这或许是刚从Vue转向React的开发者第一个能够想到的方法.Mixin一直被广泛用于各种面向对象的语言中,其作用是为单继承语言创造一种类似多重继承的效果.虽然现在React已将其放弃中,但Mixin的

  • 浅谈c#设计模式之单一原则

    单一原则: 程序设计时功能模块独立,功能单一更有助于维护和复用. 例如:个人计算机功能很多,如果想从中只拿出一个功能来制造一个新的东西是困难的.同时如果你的计算机开不机,同时你的计算器功能也不能用了. 在编程中如果一个类封装了太多功能和上面的结果是类似的. 单一职责原则 例1: 大家应该能看出来这个类图中的接口设计是有问题的,用户的属性和用户的行为没有分开.我们根据用户的属性和行为拆开这个接口. 重新拆分成两个接口,IUserBo 负责用户的属性,IUserBiz负责用户的行为.当我们实例化除U

  • 使用ES6语法重构React代码详解

    使用ES6语法重构React组件 在Airbnb React/JSX Style Guide中,推荐使用ES6语法来编写react组件.下面总结一下使用ES6 class语法创建组件和以前使用React.createClass方法来创建组件的不同. 创建组件 ES6 class创建的组件语法更加简明,也更符合javascript.内部的方法不需要使用function关键字. React.createClass import React from 'react'; const MyComponen

  • 教你用AngularJS框架一行JS代码实现控件验证效果

    如上图所示,我们需要实现如下这些验证功能: 控件都是必输控件 都需要控制最大长度 第一次打开页面,控件不能显示为错误状态 输入内容再清空后,必输控件需要显示为错误状态 只有所有输入合法后,发布按钮才能变为可用状态 通过AngularJS,我们可以很轻松的实现这些要求,只需要1行JS代码.UI样式这里采用的是Bootstrap.先上示例代码: HTML. <!DOCTYPE html> <html lang="zh-cn" ng-app="ftitApp&qu

  • 手把手教你实现微信小视频iOS代码实现

    前段时间项目要求需要在聊天模块中加入类似微信的小视频功能,这边博客主要是为了总结遇到的问题和解决方法,希望能够对有同样需求的朋友有所帮助. 效果预览: 这里先罗列遇到的主要问题:  1.视频剪裁  微信的小视频只是取了摄像头获取的一部分画面  2.滚动预览的卡顿问题  AVPlayer播放视频在滚动中会出现很卡的问题 接下来让我们一步步来实现. Part 1 实现视频录制 1.录制类WKMovieRecorder实现 创建一个录制类WKMovieRecorder,负责视频录制. @interfa

随机推荐