Skip to content

withViewModel HOC

A Higher-Order Component that connects React components to their ViewModels, providing seamless MobX integration.

API Signature

tsx
function withViewModel<VM extends AnyViewModel>(
  ViewModelClass: Class<VM>,
  config?: ViewModelHocConfig<VM>
):
  (Component: ComponentType<ComponentProps & ViewModelProps<VM>>) =>
    ComponentWithViewModel

function withViewModel<
  TViewModel extends AnyViewModel,
  TCompProps extends AnyObject = ViewModelProps<TViewModel>,
>(
  model: Class<TViewModel>,
  component: ComponentType<TCompProps & ViewModelProps<TViewModel>>,
  config?: ViewModelHocConfig<TViewModel>,
): ComponentWithViewModel<TViewModel, TCompProps>;

Usage

1. Basic Usage (Default Configuration)

tsx
export const YourComponent = withViewModel(VMClass)(ViewComponent);

export const YourComponent = withViewModel(VMClass, ViewComponent);

2. Custom Configuration

tsx
export const YourComponent = withViewModel(VMClass, {
  vmConfig: {}, // vmConfig
  ctx: {}, // internal object as source for all cache inside this HOC
  factory: (config) => new config.VM(config), // factory method for creating VM instances
  fallback: () => <div>loading</div>, // fallback component for cases when your VM is mounting\loading
  generateId, // custom fn for generate id for this VM instances
  getPayload: (props) => props.payload, // function to getting payload data from props
  id, // unique id if you need to create 1 instance of your VM
  reactHook: (allProps, ctx, viewModels) => void 0, // hook for integration inside render HOC component  
})(ViewComponent)

export const YourComponent = withViewModel(VMClass, ViewComponent, {
  vmConfig: {}, // vmConfig
  ctx: {}, // internal object as source for all cache inside this HOC
  factory: (config) => new config.VM(config), // factory method for creating VM instances
  fallback: () => <div>loading</div>, // fallback component for cases when your VM is mounting\loading
  generateId, // custom fn for generate id for this VM instances
  getPayload: (props) => props.payload, // function to getting payload data from props
  id, // unique id if you need to create 1 instance of your VM
  reactHook: (allProps, ctx, viewModels) => void 0, // hook for integration inside render HOC component  
})

Examples:

tsx
import {
  ViewModelBase,
  ViewModelProps,
  withViewModel
} from "mobx-view-model";
import { observer } from "mobx-react-lite";
import { observable, action } from "mobx";

class VM extends ViewModelBase {
  @observable
  accessor value = '';

  @action
  setValue = (value: string) => {
    this.value = value;
  }
}

const ComponentView = observer(({ model }: ViewModelProps<VM>) => {
  return (
    <div>
      <input
        value={model.value}
        onChange={e => model.setValue(e.target.value)}
      />
    </div>
  )
})

export const YourComponent = withViewModel(VM)(ComponentView);


export const AnotherComponent = withViewModel(VM, ({ model }) => {

  return (
    <div>
      <input
        className={"bg-[red]"}
        value={model.value}
        onChange={e => model.setValue(e.target.value)}
      />
    </div>
  )
})

Incompatibility with <Suspense /> and lazy()

The withViewModel HOC is not compatible with the React's built-in <Suspense /> component and lazy() function.

Using Suspense and lazy with withViewModel HOC can lead to unexpected behavior and bugs due to double/triple calls of useMemo or lazy useState hooks inside useCreateViewModel hook.

To avoid this issue, it is recommended to use withLazyViewModel HOC instead.

Released under the MIT License.