Skip to content

Base classes and utilities of mobx stores and validable mobx models.

License

Notifications You must be signed in to change notification settings

vlazh/mobx-utils

Repository files navigation

MobX Utils

npm package

Base classes and utilities of mobx stores and validable mobx models.

Example

RootStore

import { action } from 'mobx';
import { BaseRootStore, NotificationsStore, WorkerStore } from '@vlazh/mobx-utils/stores';
import { JSONModel, JSONSerializable } from '@vlazh/mobx-utils/serialization';
import AuthStore from './AuthStore';
import AppStore from './AppStore';
import SignUpStore from './SignUpStore';
import SignInStore from './SignInStore';

export type RootStoreState = Pick<Partial<RootStore>, 'appStore' | 'authStore'>;

export default class RootStore extends BaseRootStore implements JSONSerializable<RootStoreState> {
  readonly _serializable = this;

  readonly authStore: AuthStore;

  readonly appStore: AppStore;

  readonly signInStore: SignInStore;

  readonly signUpStore: SignUpStore;

  // ...

  constructor(initialState: Partial<JSONModel<RootStoreState>> = {}) {
    super();
    const appNotificationsStore = this.createNotificationsStore();
    const appWorkerStore = this.createWorkerStore();

    this.authStore = new AuthStore(
      this,
      appNotificationsStore,
      appWorkerStore,
      initialState.authStore
    );
    this.appStore = new AppStore(
      this,
      appNotificationsStore,
      appWorkerStore,
      initialState.appStore
    );

    this.signInStore = new SignInStore(this, appNotificationsStore, appWorkerStore);
    this.signUpStore = new SignUpStore(this, appNotificationsStore, appWorkerStore);
  }

  createNotificationsStore(): NotificationsStore<this, AppNotification> {
    return new NotificationsStore<this, AppNotification>(this, 10000);
  }

  createWorkerStore<TaskKeys extends string = never>(): WorkerStore<this, TaskKeys> {
    return new WorkerStore(this);
  }

  toJSON(): JSONModel<RootStoreState> {
    return {
      appStore: this.appStore.toJSON(),
      authStore: this.authStore.toJSON(),
    };
  }
}

AppStore

import { RequestableStore, NotificationsStore, WorkerStore } from '@vlazh/mobx-utils/stores';
import RootStore from './RootStore';

export default class AppStore
  extends RequestableStore<
    RootStore,
    NotificationsStore<RootStore, AppNotification>,
    WorkerStore<RootStore>
  >
  implements JSONSerializable<AppStore>
{
  readonly _serializable = this;

  // ...

  constructor(
    rootStore: RootStore,
    notifications: NotificationsStore<RootStore, AppNotification>,
    worker: WorkerStore<RootStore>,
    initialState?: JSONModel<AppStore>
  ) {
    super(rootStore, notifications, worker);
    if (initialState) {
      // ...
    }
  }

  @withRequest<AppStore>({
    // Keep live last result of call of loadData for 10min or while authStore.sessionId is changed.
    memo: { lifetime: 60 * 10, inputs: (self) => [self.rootStore.authStore.sessionId] },
  })
  loadData = flow(function* loadData(this: AppStore) {
    const { authStore } = this.rootStore;

    if (authStore.isLoggedIn) {
      const data = yield api.fetchSomeData();
      // ...
    } else {
      const data = yield api.fetchOtherData();
      // ...
    }
  });

  toJSON(): JSONModel<AppStore> {
    return {
      // ...
    };
  }
}

AppView

import React, { useEffect } from 'react';
import { observer } from 'mobx-react-lite';

function AppView({ rootStore }: AppViewProps): React.JSX.Element {
  const {
    authStore,
    appStore,
    appStore: { notifications, worker },
  } = rootStore;

  useEffect(() => {
    appStore.loadData();
  }, [appStore, authStore.isLoggedIn]);

  return (
    <RootStoreContext value={rootStore}>
      <AppLoader loading={worker.isPending()} />
      <AppNotifications notifications={notifications} position="window-top" />
    </RootStoreContext>
  );
}

export default observer(AppView);