日常使用mobx的小技巧


日常使用mobx的小技巧

由於自己開發的項目都是中小型項目,所以在技術選型上使用了mobx。但是使用過程中發現關於mobx的技術文章並不多。於是萌發出寫這篇文章的想法。請輕噴。

  • 更新控制store渲染的方法
mobx一些有關資料

中文文檔
github倉庫
npm地址

添加mobx
npm i mobx
npm i mobx-react

mobx用來操作store(也就是數據操作層,model層),而mobx-react則是用來操作view(也就是視圖層,Component層)。

mobx常用操作符
  1. observable,將JS基本數據類型、引用類型、普通對象、類實例、數組和映射,轉換為可觀察數據。
  2. action,用來修改observable的數據的動作,只有actionrunInAction才能修改observable
  3. runInAction,用來在異步的時候執行修改observable的數據的動作。例如網絡請求后修改數據。
  4. computed,根據現有的observable的值或其它計算值衍生出的值。只有在view使用了computed的值,computed才會執行計算
mobx-react常用操作符
  1. observer,將 React組件 轉變成響應式組件。
  2. inject,將組件連接到提供的stores 。一般是用來連接到上層組件提供的store或者全局store
  3. Provider,它是一個react組件,用來向下傳遞 stores。任意子組件可以使用inject來獲取Providerstore
代碼

下面會貼點自己的代碼。希望能給大家帶來一些幫助。

全局store

// 文件 index.jsx
import { Provider } from "mobx-react";
import * as stores from "../../stores";
class MainView extends Component {
  render() {
    return (
      <React.Fragment>
        <Provider {...stores}>  // 這里是全局stores的配置
          <Switch>
            {routers.map((item, index) => {
              return (
                <Route
                  exact
                  key={item.path}
                  path={item.path}
                  component={item.component}
                />
              );
            })}
          </Switch>
        </Provider>
      </React.Fragment>
    );
  }
}


// 文件 ../../stores/index.js
import aStore from "./aStore";
import bStore from "./bStore";
export { aStore, bStore};

// 文件 aStore.js
class AStore {
  @observable info = {};
  @observable line = {};
  @action
  onInfo = data => {
    this.info = data;
  };
  @action
  onLine = data => {
    this.line = data;
  };
}
const aStore = new AStore();
export default aStore;

// 組件使用aStore
@inject(all => ({
  aStore: all.aStore // 連接到aStore
}))
@observer
class Detail extends Component {
		updateInfo = () => {
			const aStore = this.props.aStore;
			aStore.onInfo(info) // 改變store屬性
		}
	  render() {
	  	const aStore = this.props.aStore; // 獲取到全局store
      return (
        <div>
        	{aStore.info.name} // 使用里面的屬性
        	<Button onClick={this.updateInfo}>改變info</Button>
        </div>
      );
  	}
}

組件內可觀察數據。

@observer
class Detail extends Component {
  @observable info = {};
  @observable index = 1;
  
  @computed get sum() {
  	return index * 4
  }
  
  @action
  onInfo = data => {
    this.info = data;
  };
  updateInfo = () => {
		this.onInfo(info) // 改變store屬性
	}
	render() {
    return (
      <div>
       {this.info.name} // 使用里面的屬性
       <Button onClick={this.updateInfo}>改變info</Button>
      </div>
    );
  }
}

最后一種是我自己項目使用的,個人覺得不錯。可以分離邏輯層和視圖層。如果想使用全局stores,直接用inject導入第一種方式注入的stores。能較好划分全局stores單業務store的職責,而不是無腦的以樹的方式全掛在index.js上面。子組件最好跟父組件用同一個store,方便溝通的同時層級不多也不會導致store太過復雜。

// 邏輯層 用來處理業務邏輯
import { observable, runInAction } from "mobx";
import { aService, bService } from "../../../services/dispatch";
import { T } from "react-toast-mobile";
class ListStore {
  @observable list = [];

  serInitData = async () => {
    try {
      T.loading();
      const data = await aService()
      runInAction("serInitData", () => {
        this.list = data || [];
      });
    } catch (error) {
      T.notify(error.message);
      console.error(error);
    } finally {
      T.loaded();
    }
  };

  serBService = async vehicleNum => {
    try {
      T.loading();
      await bService(vehicleNum);
      runInAction("serBService", () => {
        this.list = this.list.filter(params => {
          return params.vehicleNum != vehicleNum;
        });
      });
    } catch (error) {
      T.notify(error.message);
      // eslint-disable-next-line no-console
      console.error(error);
    } finally {
      T.loaded();
    }
  };
}
export default ListStore;


// 視圖層 純粹的視圖展示和操作觸發
@observer
class Detail extends Component {
  store = new ListStore();
  
  componentDidMount() {
  	// 初始化數據
    this.store.serInitData();
  }
  
  updateInfo = () => {
  	this.store.serBService(); // 調用bService
	}
	render() {
    return (
      <div>
       {this.store.list.map((item)=>{
       		return 
       		<Provider myStore={this.store} >
       			<Item key={item.guid} />
       		</Provider>
       })} // 輪詢列表
       <Button onClick={this.updateInfo}>改變info</Button>
      </div>
    );
  }
}

const Item = inject(allStores => ({
   aStore: all.aStore, // 連接到aStore
   myStore: allStores.myStore, // 連接到父組件的myStore
}))(
observer(function(props) { // 使用function方式在發送action等操作時,會帶上Item這個組件名字,能變相的看到發送的來源。使用箭頭函數則不會。而且react推薦的無狀態組件也是使用的function
  
  return (
    <div>
    	{this.props.aStore.xxxxx} // 使用全局store
    	{this.props.myStore.xxxxx} // 使用父組件的store
    </div>
  );
}));

一些備注
  1. 使用@inject后,無法通過組件的refs屬性調用其對應的方法?
if (this.tabIndex == "0") {
    // 加了@inject+@observer
    this.biddingRef.current.wrappedInstance.openFilter();
} else if (this.tabIndex == "1") {
    // 只有@observer
    this.zdRef.current.openFilter();
}
參考鏈接
// https://stackoverflow.com/questions/43847401/reactnative-mobx-how-to-access-component-refs-from-mobx
  1. mobx如何自動保存數據
import { observable, action, autorun, toJS, set } from "mobx";

function autoSave(store, save) {
  let firstRun = true;
  autorun(() => {
    // 此代碼將在每次運行任何可觀察屬性時運行
    // 對store進行更新。
    const json = JSON.stringify(toJS(store));
    if (!firstRun) {
      save(json);
    }
    firstRun = false;
  });
}
class RouteState {
  @observable state = {};
  constructor() {
    this.load();
    autoSave(this, this.save.bind(this));
  }
  load() {
    const storeTemp = sessionStorage.getItem("route_state");
    if (storeTemp) {
      const data = JSON.parse(storeTemp);
      set(this, data);
    }
  }
  save(json) {
    sessionStorage.setItem("route_state", json);
  }
  @action.bound
  actionState(_state) {
    this.state = _state;
  }
}

// 參考鏈接
https://stackoverflow.com/questions/40292677/how-to-save-mobx-state-in-sessionstorage
場景模擬

列表A中有一個倒計時,這個倒計時會改變dataListitem里面的時間戳.

列表所在的頁面有個篩選浮層,篩選浮層中有DatePicker組件

遇到的問題

DatePicker選擇其他時間1s后,時間會重置為初始設置的時間.

原因

列表A中的倒計時每秒都在改變dataList,這導致該頁面的子組件每秒都會執行render函數.導致DatePicker中的時間重置為初始時間.

解決辦法

github iss

// 新建一個包含組件.注意,這里不能用@observer將其包裹起來,
// 包含組件應該是個干凈的,由你控制是否重新渲染的組件
class FilterPopContains extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 在包含組件的shouldComponentUpdate方法中自己判斷是否要執行render函數
    if (JSON.stringify(nextProps) != JSON.stringify(this.props)) return true;
    return false;
  }
  render() {
    // 將props原封不動傳遞給邏輯組件
    return <FilterPop {...this.props} />;
  }
}
// 這個是具體的內容組件,里面有你的邏輯
@observer
class BiddingFilterPop extends Component {
  render() {
    return <div>
    	<DatePicker/>
    </div>
  }
}
  1. mobx的使用非常靈活。可以多種使用方式在同一個項目使用。並不沖突。
  2. 推薦所有的組件都observer化,這並不會造成性能損耗,反而會優化組件。
  3. 想不到了。想起來再更新吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM