區分狀態級別在組件設計中的重要性

隨着全局狀態管理庫如vuex,mobx,redux的盛行,人們愈來愈習慣把狀態一股腦丟在狀態管理庫裏面,卻忘了去劃分狀態的級別。這種濫用全局狀態管理庫的現象一直廣泛存在。vue

什麼叫濫用全局狀態管理庫?

就是沒有認識到狀態管理庫的根本做用,或者說咱們何時才須要狀態管理庫?react

拿 react 來講,react 是有組內狀態的,狀態能夠經過 props 傳遞。可是,但當 app 比較龐大的時候,兄弟組件,遠親組件這些的交流就變得困難起來, 它們必須依賴相同的父組件來完成信息的傳遞。這時,就是咱們使用狀態管理庫的時候。vuex

可是,不少人把全部狀態都往 redux 裏面丟,雖然這方便了開發,但缺點卻很明顯:redux

  1. 組件很難複用:由於狀態保存在global,因此只有一份。你在多個地方使用了這個組件,但由於狀態只有一份,這個組件的多個實例是相互影響的,這很糟糕。
  2. 耦合度高:根據高內聚低耦合的設計原則,咱們在設計組件的時候應該保證這個組件有獨立的功能,經過props來跟外界交流。但若是把組件的狀態放在全局 model而不是放在組件內部,就違背了內聚這一規則。其次,也提供了讓其餘地方修改這份model的能力,使用這個組件的人不得不擔憂是否有地方影響到這份model,這很糟糕。

正確的作法

劃分好狀態的等級,儘可能把狀態放在組件內。當遇到須要共享組內狀態的時候,提高狀態到父級或者全局狀態管理庫。api

你能夠把view、model、loadData邏輯都塞進這個組件,這樣它便有了獨立功能。它把複雜度隱藏在內部,而後向外界暴露一些api來提供本身的能力。app

例如一個列表組件:性能

// 方案一
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';

export default function ListDemo({requestId}){
  // model
  const [data,setData] = useState([]);
  const [visible,setVisible] = useState(false);

  useEffect(()=>{
    // services 層
    getData().then(data=>{
      setData(data)
    });
    /**
     * 當requestId變化時,列表會從新請求
     * 這裏的requestId是組件向外界暴露的一個api
     **/
  },[requestId])

  useEffect(()=>{
    if(visible===true){
      // clearState
      setVisible(false);
    }
  },[requestId])

  return (
    // view
    <div>
      {
        data.map(item=><li>{item}</li>
      }
      {
        visible && (
          <div>
            this is a modal
          </div>
        )
      }
    </div>
    )
  )
}

// app.jsx
<ListDemo />
複製代碼

這種組件設計的特色是,組件能夠重置自身狀態的時機是由自身控制的。若是你以爲這樣麻煩,你能夠把重置自身狀態的時機交給外部,經過key來 「銷燬組件」=>「從新渲染組件」。上面的代碼能夠簡化成:this

// 方案二
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';

export default function ListDemo({requestId}){
  // model
  const [data,setData] = useState([]);
  const [visible,setVisible] = useState(false);

  useEffect(()=>{
    // services 層
    getData().then(data=>{
      setData(data)
    });
  },[])

  return (
    // view
    <div>
      {
        data.map(item=><li>{item}</li>
      }
      {
        visible && (
          <div>
            this is a modal
          </div>
        )
      }
    </div>
    )
  )
}

// app.jsx
/*
*當requestId變化時,ListDemo會從新渲染
*/
<ListDemo key={requestId} />
複製代碼

方案二的代碼比較整潔,且出錯率比方案一低,可是方案二存在從新渲染組件的一個環節,性能開支會比方案一多一點點(大部分狀況你均可以忽略不計)。不少狀況下,我都會採用方案二。spa

相關文章
相關標籤/搜索