mobx-view-model
Version:
MobX ViewModel power for ReactJS
181 lines (180 loc) • 5.86 kB
JavaScript
/* eslint-disable @typescript-eslint/no-unused-vars */
import { action, comparer, computed, observable, runInAction } from 'mobx';
import { isShallowEqual } from 'yummies/data';
import { startViewTransitionSafety } from 'yummies/html';
import { applyObservable, mergeVMConfigs, } from '../config/index.js';
export class ViewModelBase {
vmParams;
abortController;
unmountSignal;
id;
_isMounted = false;
_isUnmounting = false;
_payload;
vmConfig;
isPayloadEqual;
/**
* @deprecated use `vmParams`. This property will be removed in next major release
* Reason: this word is very useful for users, so `vmParams` is more library-targered naming
*/
params;
props;
constructor(vmParams) {
this.vmParams = vmParams;
this.params = vmParams;
this.id = vmParams.id;
this.vmConfig = mergeVMConfigs(vmParams.config ?? vmParams.vmConfig);
this._payload = vmParams.payload;
this.props = vmParams.props ?? {};
this.abortController = new AbortController();
this.unmountSignal = this.abortController.signal;
if (this.vmConfig.comparePayload === 'strict') {
this.isPayloadEqual = comparer.structural;
}
else if (this.vmConfig.comparePayload === 'shallow') {
this.isPayloadEqual = isShallowEqual;
}
else if (typeof this.vmConfig.comparePayload === 'function') {
this.isPayloadEqual = this.vmConfig.comparePayload;
}
const annotations = [
['_isMounted', observable.ref],
['_isUnmounting', observable.ref],
['isMounted', computed],
['isUnmounting', computed],
['parentViewModel', computed],
['mount', action.bound],
['didMount', action],
['unmount', action.bound],
['didUnmount', action],
['willUnmount', action],
['setPayload', action],
];
if (this.vmConfig.payloadObservable !== false) {
annotations.push([
'_payload',
observable[this.vmConfig.payloadObservable ?? 'ref'],
]);
}
if (this.vmConfig.payloadComputed) {
if (this.vmConfig.payloadComputed === 'struct') {
annotations.push(['payload', computed.struct]);
}
else {
annotations.push([
'payload',
computed({
equals: this.vmConfig.payloadComputed === true
? undefined
: this.vmConfig.payloadComputed,
}),
]);
}
}
applyObservable(this, annotations, this.vmConfig.observable.viewModels);
}
get payload() {
return this._payload;
}
get viewModels() {
if (process.env.NODE_ENV !== 'production' && !this.vmParams.viewModels) {
console.error('accessing to viewModels is not possible. [viewModels] param is not setted during to creating instance ViewModelBase');
}
return this.vmParams.viewModels;
}
get isMounted() {
return this._isMounted;
}
get isUnmounting() {
return this._isUnmounting;
}
willUnmount() {
this._isUnmounting = true;
}
/**
* Empty method to be overridden
*/
willMount() {
/* Empty method to be overridden */
}
/**
* The method is called when the view starts mounting
*/
mount() {
this.vmConfig.onMount?.(this);
startViewTransitionSafety(() => {
runInAction(() => {
this._isMounted = true;
});
}, {
disabled: !this.vmConfig.startViewTransitions.mount,
});
this.didMount();
}
/**
* The method is called when the view was mounted
*/
didMount() {
/* Empty method to be overridden */
}
/**
* The method is called when the view starts unmounting
*/
unmount() {
this.vmConfig.onUnmount?.(this);
startViewTransitionSafety(() => {
runInAction(() => {
this._isMounted = false;
});
}, {
disabled: !this.vmConfig.startViewTransitions.unmount,
});
this.didUnmount();
}
/**
* The method is called when the view was unmounted
*/
didUnmount() {
this.abortController.abort();
runInAction(() => {
this._isUnmounting = false;
});
}
/**
* The method is called when the payload of the view model was changed
*
* The state - "was changed" is determined inside the setPayload method
*/
payloadChanged(payload, prevPayload) {
/* Empty method to be overridden */
}
/**
* Returns the parent view model
* For this property to work, the getParentViewModel method is required
*/
get parentViewModel() {
return this.getParentViewModel(this.vmParams.parentViewModelId);
}
/**
* The method is called when the payload changes in the react component
*/
setPayload(payload) {
if (!this.isPayloadEqual?.(this._payload, payload)) {
startViewTransitionSafety(() => {
runInAction(() => {
this.payloadChanged(payload, this._payload);
this._payload = payload;
});
}, {
disabled: !this.vmConfig.startViewTransitions.payloadChange,
});
}
}
/**
* The method of getting the parent view model
*/
getParentViewModel(parentViewModelId) {
return (this.vmParams.parentViewModel ??
this.viewModels?.get(parentViewModelId));
}
}