博客 / 詳情

返回

彙總mobx奇淫技巧

簡化action屬性更新

通常情況下,store中會有很多屬性,其中有些屬性更新會很頻繁,每一個屬性都需要寫一個action函數去更新,當這種需要變更的屬性越多時,會導致store中代碼量異常的龐大,可讀性也會降低。就如下面代碼一樣:

class TestStore {
  @observable info = {} as IInfo;
  
  @observable list = [] as IList;
  
  @action
  setInfo = (value: IInfo) => {
    this.info = value;
  }
  
  @action
  setList = (value: IList) => {
    this.list = value;
  }
}

引入typescriptkeyof關鍵字使用,可以將上述action函數簡化如下:

class TestStore {
  @observable info = {} as IInfo;
  
  @observable list = [] as IList;
  
  @action
  setValue = <T extends keyof TestStore>(key: T, value: this[T]) => {
    this[key] = value;
  }
}

簡化store之間的相關依賴

callback實現Store之間的通信

比如StoreA中某個方法執行後需要調用StoreB中的方法

// /stores/StoreA.ts
class StoreA {
  @action
  methodA = (cb: () => void) => {
    // coding...
    if (cb) cb();
  }
}

// /stores/StoreB.ts
class StoreB {
  @action
  methodB = () => {
    // coding...
  }
}

// /stores/index.ts
export default {
  storeA: new StoreA(),
  storeB: new StoreB(),
}

頁面調用

// index.tsx
import React from 'react';

import stores from './stores/index.ts';
import Main from './components/Main.tsx';

const Index = () => {
  return <Provider {...stores}>
    <Main {...props}></Main>
  </Provider>
}

// Main.tsx
import React from 'react';

interface IProps {
  storeA?: StoreA;
  storeB?: StoreB;
}

const Main = (props: IProps) => {
  const { storeA, StoreB } = props;
  
  const { methodA } = storeA;
  
  const { methodB } = storeB;
  
  const handleClick = () => {
    methodA(methodB);
  }

  return <span onClick={handleClick}></span>
}

promise實現Store之間的通信

// /stores/StoreA.ts
class StoreA {
  @action
  methodA = () => {
    return new Promise((resolve) => {
      // coding...
      resolve();
    })
  }
}

頁面調用

const Main = (props: IProps) => {
  const { storeA, StoreB } = props;
  
  const { methodA } = storeA;
  
  const { methodB } = storeB;
  
  const handleClick = () => {
    methodA().then(() => {
      methodB();
    });
  }

  return <span onClick={handleClick}></span>
}

通過根級Store調度子級Store實現通信

image.png
引入根級store,將上面store組織方式改成如下形式:

// /stores/StoreA.ts
class StoreA {
  rootStore: RootStore;

  constructor(rootStore) {
    this.rootStore = rootStore;
  }

  @action
  methodA = () => {
    // coding...
    this.rootStore.storeB.methodB();
  }
}

// /stores/StoreB.ts
class StoreB {
  rootStore: RootStore;
  
  constructor(rootStore) {
    this.rootStore = rootStore;
  }

  @action
  methodB = () => {
    // coding...
  }
}

class RootStore {
  storeA: StoreA;
  
  storeB: StoreB;
  
  constructor() {
    this.storeA = new StoreA(this),
    this.storeB = new StoreB(this),
  }
}


// /stores/index.ts
export default {
  rootStore: new RootStore(),
}

頁面調用

const Main = (props: IProps) => {
  const { storeA, StoreB } = props;
  
  const { methodA } = storeA;
  
  const { methodB } = storeB;
  
  const handleClick = () => {
    methodA();
  }

  return <span onClick={handleClick}></span>
}  

降低store的顆粒度

image.png
有些項目頁面的業務邏輯、交互異常的複雜,如果把這些業務邏輯都堆在一個store中將會使得store的代碼量異常的龐大,就如上圖store有着上千行的代碼量。這將會嚴重降低代碼的可讀性,如果繼續迭代甚至會影響後人的開發效率。

當然,我們可以上面拆分成多個store來解決問題,這種場景不是我本節考慮的,我想講述的是以下這種場景:

image.png

假如我們要做這麼一個表格,並且後端給的數據結構是這樣子的:

image.png

quote數組中的每一個對象對應着每一個報價條目

image.png

如果每條報價條目數據需要改變時,就需要去遍歷quote數組找到對應的報價條目對象,重新賦值,寫一個函數就行了。如果報價條目相關的業務邏輯(校驗、變更值等等)特別的多呢?那麼也要書寫很多的函數去實現,這些跟報價條目相關的業務代碼都堆在一個store中肯定會使store更加的臃腫。

肯定有同學會説,把這部分拆分到一個獨立的store中不就可以了嗎?但是表格數據作為一個整體,這種業務拆分是不容易實現的,那麼有沒有其他方法呢?

肯定有的,在mobx中每一個報價條目對象都是一個observable對象,我們可以把這個對象抽出來放到另一個store中,而之前的store存放的只是一個引用,描述的可能不太清楚,附上代碼説明一下:

// quotation.ts
class Quotation {
  @observable  quoteId: string = '';
  
  @observable  partNum: string = '';
  
  @observable  partName: string = '';
  
  constructor(quotation: any) {
    Object.assign(this, {...quotation});
  }
  
  @action
  ...
}
// tableStore.ts
class TableStore {
  @observable tableData: ITable = [] as ITable;
  
  assemble = (needResults: any, quotations: any) => {
    const data: ITable = [];
    needResults.forEach(v => {
      quotations.forEach(p => {
        if (p.demandItemId === v.demandItemId) {
          data.push({
            need: v,
            quote: p.result.map(o => {
              return new Quotation(o);
            })
          })
        }
      })
    })
  }
  
  /** 獲取報價項次、報價結果 */
  @action
  fetchTableData = async () => {
    Promise.all([fetchNeedResults(), fetchQuotationResults()]).then((res) => {
      if (res[0].code === 200 && res[1].code === 200) {
        const needResults = res[0].data;
        const quotations = res[1].data;
        /** 組裝表格數據 */
        this.tableData = this.assemble(needResults, quotations);
      }
    });
  };
}

這麼設計store的組織方式可以更加靈活的控制store的顆粒度,同時也能很好的避免store的巨大化以及多人操作同一個store的場景。比如圖中可以類似將“表格部分”拆分出“需求部分”、“報價部分”,當多人同時開發時,每一個人只需要負責自己模塊中的樣式、業務邏輯、數據存儲,在一定程度上形成了一個閉環,對於每個開發來説都是有益的,而且這樣一種組織代碼的方式也會增強代碼的可讀性以及複用性,方便後期的維護。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.