Skip to content

Getting Started

Install

bash
npm i @lomray/react-mobx-manager @lomray/consistent-suspense

@lomray/consistent-suspense is used to generate stable ids inside Suspense boundaries.

Choose store id strategy

Store ids are required for stable store identity.

If you do not want to manually assign an id to every store, use one of the automatic strategies:

  1. Keep class names and function names in your bundler.
  2. Define static id on stores manually.
  3. Use the Vite plugin from this package.

Recommended:

  • use keep_classnames / keep_fnames when possible
  • use the Vite plugin if you are on Vite and want the smoothest setup
  • use manual ids only if you want to control ids explicitly store by store

Keep class names

Good default if you want to avoid manual ids.

js
terserOptions.keep_classnames = true;
terserOptions.keep_fnames = true;

React Native:

js
module.exports = {
  transformer: {
    minifierConfig: {
      keep_classnames: true,
      keep_fnames: true,
    },
  },
};

Manual id

Choose this only if you want to explicitly manage ids yourself.

ts
class UserStore {
  static id = 'user';
}

Vite plugin

ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import MobxManager from '@lomray/react-mobx-manager/plugins/vite/index';

export default defineConfig({
  plugins: [react(), MobxManager()],
});

If you use Vite, this is the recommended option.

Create a manager

tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ConsistentSuspenseProvider } from '@lomray/consistent-suspense';
import { Manager, StoreManagerProvider } from '@lomray/react-mobx-manager';
import App from './app';
import MyApiClient from './services/my-api-client';

const apiClient = new MyApiClient();
const storeManager = new Manager({
  storesParams: { apiClient },
});

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <ConsistentSuspenseProvider>
      <StoreManagerProvider storeManager={storeManager} shouldInit>
        <App />
      </StoreManagerProvider>
    </ConsistentSuspenseProvider>
  </React.StrictMode>,
);

Connect stores to a component

Global store example:

tsx
import { withStores, Manager } from '@lomray/react-mobx-manager';
import { makeObservable, observable, action } from 'mobx';
import type { IConstructorParams, StoresType } from '@lomray/react-mobx-manager';

class UserStore {
  static id = 'user';
  static isGlobal = true;

  public name = 'Matthew';
  private apiClient: MyApiClient;

  constructor({ getStore, apiClient }: IConstructorParams) {
    this.apiClient = apiClient;
    // this.otherStore = getStore(SomeOtherStore);

    makeObservable(this, {
      name: observable,
      setName: action.bound,
    });
  }

  public setName(name: string): void {
    this.name = name;
  }
}

const stores = {
  userStore: UserStore,
};

type TProps = StoresType<typeof stores>;

const User = ({ userStore: { name } }: TProps) => {
  return <div>{name}</div>;
};

export default withStores(User, stores);

Relative store example:

tsx
import { withStores } from '@lomray/react-mobx-manager';
import { makeObservable, observable, action } from 'mobx';

class SomeOtherStore {
  public value = '';

  constructor() {
    makeObservable(this, {
      value: observable,
      setValue: action.bound,
    });
  }

  public setValue(value: string): void {
    this.value = value;
  }
}

const stores = {
  someOtherStore: SomeOtherStore,
};

const SomeComponent = ({ someOtherStore: { value, setValue } }) => {
  return <button onClick={() => setValue('next')}>{value}</button>;
};

export default withStores(SomeComponent, stores);

Parent lookup helper

Prefer parentStore(Store) over inline config for better DX:

ts
import { parentStore } from '@lomray/react-mobx-manager';

const stores = {
  someOtherStore: parentStore(SomeOtherStore),
  userStore: UserStore,
};

Use this when a child component should reuse a relative store created in an ancestor component.

Released under the MIT License.