next.js with mobx

github

關於狀態管理

爲何須要狀態管理?
  • 由於層次深,很不爽
  • 數據和行爲分離,是個不錯的實踐
爲何是 mobx,而不是 redux或者直接 context
  • redux 用很久了,嘗試下新的,樣板代碼太多了,很不爽
  • context 不推薦不是?
  • 什麼函數式,我的見智
  • 據說mobxvuex一個道理,我試試
  • mobx 代碼好少,哇,能夠提早下班了,爽

mobx 介紹

這個不用介紹了吧,本身googlevue

next 介紹

這個我也無論, ssr、同構嘛,也就那回事。react

next 引入mobx

配置啥的(for decorators)

  • babel 6
npm i --save-dev babel-plugin-transform-decorators-legacy

.babelrcgit

{
    "presets": ["es2015", "stage-1"],
  "plugins": ["transform-decorators-legacy"]
}
  • babel 7
// .babelrc
{
  "presets": [
    "react",
    "es2015",
    "stage-1"
  ],
  "plugins":[
      "transform-decorators",
      ["transform-class-properties", { "loose": true}]
  ]
}

// package.json
{
    "babel-core": "7.0.0-beta.3",
    "babel-preset-es2015": "7.0.0-beta.3",
    "babel-preset-react": "7.0.0-beta.3",
    "babel-preset-stage-1": "7.0.0-beta.3",
    "babel-plugin-transform-decorators": "^6.24.1"
}

兩個方案

  • 統一管理多個 store
import A from './mobx/A'
import B from './mobx/B'

const aStore = new A();
const bStore = new B();

const stores = {
    aStore,
    bStore,
}

class App extends Component {
    render() {
        return (
            <Provider {...stores}>
                <Home/>
            </Provider>
        );
    }
}

export default App;

如上,簡單的一種引用方式。next 改造同方案二。github

  • 單 page 單store

因爲在我項目中,各個模塊不耦合,路由間不想幹,因此各自處理就ok。
ssr 中要解決的一個很大的問題就是 server 和 client 之間狀態同步的問題。以下:vuex

//  with-mobx-state.js

import React from "react";
import { Provider } from "mobx-react";

export default (initializeStore, storeName, initState = {}) => App => {
  const isServer = typeof window === "undefined";

    // cache
  function getOrCreateStore(initialState = initState) {
    if (isServer) {
      return initializeStore(isServer, initialState);
    }

    if (!window[storeName]) {
      window[storeName] = initializeStore(isServer, initialState);
    }
    return window[storeName];
  }

  return class AppWithMobx extends React.Component {
    static async getInitialProps(ctx) {
      const mobxStore = getOrCreateStore();
      ctx.mobxStore = mobxStore;

      let appProps = {};
      if (typeof App.getInitialProps === "function") {
        appProps = await App.getInitialProps.call(App, ctx);
      }

      return {
        ...appProps,
        initialMobxState: mobxStore
      };
    }

    constructor(props) {
      super(props);
      // sync
      this.mobxStore = getOrCreateStore(props.initialMobxState);
    }

    render() {
      const store = { [storeName]: this.mobxStore };
      return (
        <Provider {...store}>
          <App {...this.props} {...store} />
        </Provider>
      );
    }
  };
};

// testStore.js
import { action, observable } from "mobx";

// 注意模塊內變量的做用範圍: 模塊內是全局的,模塊間是獨立的
let store = null;

class Store {
  @observable lastUpdate;
  @observable light = false;

  constructor(isServer, lastUpdate) {
    this.lastUpdate = lastUpdate;
  }

  @action
  start = () => {
    this.timer = setInterval(() => {
      this.lastUpdate = Date.now();
      this.light = true;
    }, 1000);
  };

  stop = () => clearInterval(this.timer);
}

export default function initializeTestStore(
  isServer,
  { lastUpdate = Date.now() }
) {
  if (isServer) {
    return new Store(isServer, lastUpdate);
  } else {
    if (store === null) {
      store = new Store(isServer, lastUpdate);
    }
    return store;
  }
}

// test.js
import React, { Component } from "react";
import { Card, Button } from "antd";
import { inject, observer } from "mobx-react";
import moment from "moment";
import withMobxStore from "../../decorators/with-mobx-store";
import initializeTestStore from "./testStore";

@withMobxStore(initializeTestStore, "testStore")
class MobxTest extends Component {
  static async getInitialProps(ctx) {
    return {
      title: "mobx test",
      PageHeaderVisible: true
    };
  }

  render() {
    return (
      <Card title="I'm parent">
        <TestChild1 />
        <TestChild2 />
      </Card>
    );
  }
}
export default MobxTest;

@inject("testStore")
@observer
class TestChild1 extends Component {
  render() {
    const { testStore = {} } = this.props;
    return (
      <Card title="I'm child1">
        <code>{JSON.stringify(testStore)}</code>
        <br />
        <br />
        <Button onClick={testStore.start}>start</Button>
      </Card>
    );
  }
}

@inject("testStore")
@observer
class TestChild2 extends Component {
  render() {
    const { testStore } = this.props;
    console.log("testStore.lastUpdate", testStore.lastUpdate);
    return (
      <Card title="I'm child2" style={{ marginTop: 20 }}>
        {/*<code>{testStore.lastUpdate}</code>*/}
        <br />
        <br />
        <Button onClick={testStore.stop}>stop</Button>
        <p>{moment(testStore.lastUpdate, "x").format("YYYY-MM-DD HH:mm:ss")}</p>
      </Card>
    );
  }
}

留有問題:上邊initState實際上是沒有意義的。
解決方案: 沒想到。求大佬指教。npm

TL;DR

  • 存在即合理
  • 對症下藥。別再說啥啥啥比啥啥啥好了。
  • 能快速抓老鼠的都是好貓。
相關文章
相關標籤/搜索