
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
mobx-react-viewmodel
Advanced tools
Tiny package (~0.9 KB) which can make a huge difference for your React/Mobx app architecture.
Tiny package (~0.9 KB) which can make a huge difference for your React/Mobx app architecture.
Simple hooks useViewModel and useViewModelFactory allow you to move all component's state and logic from the render function to a separate stateful view-model class. Which unleashes the full power of Mobx and helps you escape React Hooks Hell.
Written in TypeScript and 100% type-safe 🎯
npm install mobx-react-viewmodel --save
or
yarn add mobx-react-viewmodel
import { observer } from 'mobx-react'
import { useViewModel } from 'mobx-react-viewmodel'
class MyViewModel {
@observable
title: string = '';
constructor() {
makeObservable(this);
}
@action
setTitle(title: string) {
this.title = title;
}
}
const MyComponent = observer(() => {
const viewModel = useViewModel(MyViewModel); // MyViewModel instance will be created only once on first render
return (
<div>
<input
type="text"
value={viewModel.title}
onChange={e => viewModel.setTitle(e.target.value)}
placeholder="Type title..."
/>
<p>Title: {viewModel.title}</p>
</div>
);
});
@observable / @computed / @action / reaction / when primitives for powerful reactive programminguseState / useEffects / useCallback / useMemo anymore (goodbye spaghetti code, stale closure bugs and dependency list for every hook)// MyPageViewModel.ts
import { action, comparer, computed, makeObservable, observable, reaction } from 'mobx';
import { User, UsersStore } from 'our/users/module'
interface MyPageViewModelProps {
userId: string;
}
class MyPageViewModel extends ViewModel<MyPageViewModelProps> {
@observable
user?: User;
constructor(props: MyPageViewModelProps, private usersStore: UsersStore) {
super(props);
makeObservable(this);
}
// is invoked immediately after a component is mounted
init(){
this.disposers.push(
reaction(
() => this.props.userId,
userId => this.getUserFromStore(userId),
{ fireImmediately: true }
)
);
}
// is invoked immediately before a component is unmounted and destroyed
dispose() {
super.dispose();
this.user = undefined;
}
@action
updateField = (field: keyof User, value: string) => {
if (!this.user) return;
this.user[field] = value;
}
@action
save = () => {
if (!this.user) return;
this.usersStore.updateUser(this.user);
}
@computed
isPristine() {
const storeUser = this.usersStore.getUser(this.props.userId);
return this.user && storeUser && comparer.shallow(this.user, storeUser);
}
@action
private getUserFromStore = (userId: string) => {
const user = this.usersStore.getUser(userId);
if (user) {
this.user = { ...user };
} else {
console.error(`Couldn't find user: ${userId}`);
}
}
}
// MyPage.tsx
import { observer } from 'mobx-react'
import { useViewModel } from 'mobx-react-viewmodel'
import { useParams } from 'react-router-dom';
import { useStores } from 'our/stores/path';
const MyPage = observer(() => {
// Get the userId param from the URL.
const { userId } = useParams();
// Get our custom store from hook or context
const { userStore } = useStores();
const viewModel = useViewModelFactory(
props => new MyPageViewModel(props, userStore), // Factory-function will be called only once on first render
{ userId } // These `props` are reactive. It will be passed and updated in view-model every time it changes without creating new instance of view-model
);
//** or
// const viewModel = useViewModel(
// MyPageViewModel,
// { userId },
// [userStore] // Extra dependencies for the view-model constructor
// );
return (
<div>
<input
type="text"
value={viewModel.user.first_name}
onChange={e => viewModel.updateField('first_name', e.target.value)}
placeholder="First Name"
/>
<input
type="text"
value={viewModel.user.last_name}
onChange={e => viewModel.updateField('last_name', e.target.value)}
placeholder="Last Name"
/>
<button type="button" disabled={viewModel.isPristine} onClick={viewModel.save}>Save</button>
</div>
);
});
You can find more examples here.
useViewModel(ViewModelClass, props?, args?)Basic hook that creates an instance of the ViewModelClass on the first render and keeps it alive during all further renders.
If props is passed, it will be set to the ViewModelClass instance props property every time it changes.
If args is passed, it will be passed to the ViewModelClass constructor on the first render. You should use only permanent references to the object there, for example, a reference to singleton objects.
If ViewModelClass implements init() or dispose() methods, they will be called on the component's mount and unmount events.
useViewModelFactory(factoryFn, props?)Hook for more advanced view-model instantiation. It allows to inject extra dependencies to the view-model constructor inside the custom factoryFn. Life-cycle and props update logic is the same as with useViewModel
ViewModelBase class with observable props property which is automatically updated by useViewModel or useViewModelFactory. Also, it has a built-in disposers array for storing all disposer-functions from reaction, when, or any other custom ones that you need to call on unmount. You can extend your custom view-model classes from this class for cutting down boilerplate code.
FAQs
Tiny package (~0.9 KB) which can make a huge difference for your React/Mobx app architecture.
The npm package mobx-react-viewmodel receives a total of 366 weekly downloads. As such, mobx-react-viewmodel popularity was classified as not popular.
We found that mobx-react-viewmodel demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.