最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何应用 SOLID 原则整理 React 代码之单一原则

    正文概述 掘金(桃翁)   2021-05-19   395

    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. 远程数据的获取

    2. 数据过滤

    3. 复杂的状态管理

    4. 复杂的 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. 从远程数据源获取数据

    2. 过滤数据

    让我们把获取远程数据的逻辑提取到一个单独的钩子,这个钩子的名字是 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 代码之单一原则

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元