解決React應用界面開發常見痛點(一)業務邏輯與UI分離

前言:本系列是針對於React在界面開發痛點的一些解決方案,只是React應用中偏向展現的一環前端

構建一個業務與UI分離的react應用

本篇是基於HOC方案並未使用Hooksreact

業務邏輯與UI

在編寫一個react組件前,咱們必定要弄清兩件事。算法

  1. 什麼是UI?
  2. 什麼是業務邏輯?

UI:組件的具體展現元素,通俗點就是組件的長相。接受到合理的數據就能夠展現出一個合格的組件。網絡

業務邏輯:獲取數據、發送請求等等有比較明確的獨特業務的邏輯。學習

爲何要業務邏輯與UI分離?

在編寫react組件的時候,常常會出現業務邏輯類似,UI基本相同的組件,可能只是獲取的數據方式有一些不一樣。this

for example: 掘金的感興趣小冊列表爲例子spa

  1. 最外層的紅框爲這個小冊的組件Booklet
  2. 左側的是小冊的封面
  3. 右側上方的是小冊的名稱
  4. 購買人數
  5. 小冊的連接

以上這些都是Booklet組件的UI元素計算機網絡

在Component中:code

Booklet組件只須要知道如下數據就能夠正常工做:component

  1. 小冊的封面(imageUrl)
  2. 小冊的名稱(name)
  3. 購買人數(buyerNums)
  4. 連接(link)

UI組件並不須要知道:

  1. 如何獲取數據,(如何根據小冊id如何獲取到的)
  2. 數據什麼時候回來,(請求結束填充數據)
  3. 請求的具體處理,(請求開始、結束、容錯、何時開始loading等等)

以上這些應該在哪裏處理?

在Container中

  1. 根據小冊ID獲取數據。
  2. 判斷請求的當前狀態。(loading、error、success、cancel)。
  3. 製做好數據傳遞給Component

想要分離UI與業務邏輯時遇到的常見痛點

  1. 生命週期中包含請求代碼,很差抽離。
  2. 內部state致使組件後續擴展能力差。

如何解決以上問題。

recompose能夠合理解決以上問題。

在傳統的寫法中:

path: components/card/index.jsx

class Card extends React.PureComponent {
    state = {
        name: undefined,
        link: undefined,
        buyerNum: undefined,
        logoUrl: undefined,
    }

    componentDidMount() {
        getCardById(this.props.id).then(card => {
            const {name ,link, buyerNum, logoUrl} = card;
            this.setState({
                name,
                link,
                buyerNum,
                logoUrl,
            })
        });
    }

    render() {
        const {name ,link, buyerNum, logoUrl} = this.state;
        return (
            <div className="card"> <div className="logo"> <img src={logoUrl} alt={name} /> </div> <div className="name">{name}</div> <div className="buyer-num"> {buyerNum}人已購買 </div> <div className="booklet-link"> <a href={link}>試讀</a> </div> </div> ) } } 複製代碼

上面的代碼就是標準的業務與UI不分離

難以維護的影響

後續需求仍然使用這個卡片組件的UI,可是變成了卡片列表包含多個卡片,使用卡片id獲取卡片數據的業務邏輯就很不合理。

如何解決?

UI與業務分離

UI層

path: components/card/index.jsx

const Card = ({name, logoUrl, buyerNum, link}) => {
    return (
        <div className="card"> <div className="logo"> <img src={logoUrl} alt={name} /> </div> <div className="name">{name}</div> <div className="buyer-num"> {buyerNum}人已購買 </div> <div className="booklet-link"> <a href={link}>試讀</a> </div> </div> ) } 複製代碼

業務邏輯層

path: containers/Card/index.js

import {lifecycle, compose, withState, withProps} from 'recompose';
import Card from '../../components/Card';

// recompose提供的建立state的hoc
const withCardState = withState(
    'card', 'handleUpdateCard', {}
);

// recompose提供的再次處理props的hoc
const withCardProps = withProps(
    ({card}) => ({
        ...card,
    })
)

// recompose提供的生命週期鉤子的hoc
const withLifecycle = lifecycle({
    componentDidMount() {
        getCardById().then(card => {
            this.props.handleUpdateCard(card);
        })
    }
});

// compose 從右邊往左邊執行嵌套HOC的邏輯 至關於lodash的flowRight,順序很關鍵。
export default compose(
    withCardState,
    withCardProps,
    withLifecycle,
)(Card);
複製代碼

新的代碼種Card的UI組件再也不被任何業務邏輯干擾。Card的container包含了本次根據id獲取卡片的業務邏輯。若是後續須要卡片列表。只須要一個CardList的container去獲取數據,渲染Card的UI組件。

UI與業務分離的思路已經講完了。

下一期會講一講reselect這個簡潔的第三方庫如何減小react組件無心義的render。

最後打個廣告(咱們一塊兒維護的學習公衆號)

公衆號主要面向的是初級/應屆生。內容包含咱們從應屆生轉換爲職場開發所踩過的坑,以及咱們每週的學習計劃和學習總結。 內容會涉及計算機網絡算法等基礎;也會涉及前端,後臺,Android等內容~

求關注,不迷路
相關文章
相關標籤/搜索