mobx-view-model
Version:
MobX ViewModel power for ReactJS
122 lines (121 loc) • 4.16 kB
JavaScript
/* eslint-disable react-hooks/rules-of-hooks */
import { useContext, useLayoutEffect, useRef, useState } from 'react';
import { viewModelsConfig } from '../config/global-config.js';
import { ActiveViewModelContext } from '../contexts/active-view-context.js';
import { ViewModelsContext } from '../contexts/view-models-context.js';
import { useIsomorphicLayoutEffect } from '../lib/hooks/use-isomorphic-layout-effect.js';
import { generateVMId } from '../utils/create-vm-id-generator.js';
const useCreateViewModelSimple = (VM, payload) => {
const viewModels = useContext(ViewModelsContext);
const lastInstance = useRef(null);
const [instance] = useState(() => {
if (lastInstance.current) {
return lastInstance.current;
}
const instance = new VM();
viewModels?.markToBeAttached(instance);
if (viewModels && instance.linkStore) {
instance.linkStore(viewModels);
}
return instance;
});
if ('setPayload' in instance) {
useLayoutEffect(() => {
instance.setPayload(payload);
}, [payload]);
}
useIsomorphicLayoutEffect(() => {
if (viewModels) {
viewModels.attach(instance);
return () => {
viewModels.detach(instance.id);
lastInstance.current = null;
};
}
else {
instance.mount?.();
return () => {
instance.unmount?.();
lastInstance.current = null;
};
}
}, []);
return instance;
};
/**
* Creates new instance of ViewModel
*
* [**Documentation**](https://js2me.github.io/mobx-view-model/react/api/use-create-view-model.html)
*/
export function useCreateViewModel(VM, ...args) {
const [payload, config] = args;
if (!('willMount' in VM.prototype) &&
!('payloadChanged' in VM.prototype) &&
!('willUnmount' in VM.prototype)) {
// scenario for ViewModelSimple
return useCreateViewModelSimple(VM, payload);
}
const idRef = useRef('');
const viewModels = useContext(ViewModelsContext);
const parentViewModel = useContext(ActiveViewModelContext) || null;
const ctx = config?.ctx ?? {};
if (!idRef.current) {
idRef.current =
viewModels?.generateViewModelId({
...config,
ctx,
VM,
parentViewModelId: parentViewModel?.id,
}) ??
config?.id ??
generateVMId(ctx);
}
const id = idRef.current;
const instanceFromStore = viewModels ? viewModels.get(id) : null;
const instanceRef = useRef(null);
if (!instanceRef.current) {
if (instanceFromStore) {
instanceRef.current = instanceFromStore;
}
else {
const configCreate = {
...config,
vmConfig: config?.config ?? config?.vmConfig,
id,
parentViewModelId: parentViewModel?.id,
payload: payload ?? {},
VM,
viewModels,
parentViewModel,
ctx,
};
viewModels?.processCreateConfig(configCreate);
const instance = config?.factory?.(configCreate) ??
viewModels?.createViewModel(configCreate) ??
viewModelsConfig.factory(configCreate);
instanceRef.current = instance;
instance.willMount();
viewModels?.markToBeAttached(instance);
}
}
const instance = instanceRef.current;
useIsomorphicLayoutEffect(() => {
if (viewModels) {
viewModels.attach(instance);
return () => {
viewModels.detach(instance.id);
instanceRef.current = null;
};
}
else {
instance.mount();
return () => {
instance.willUnmount();
instance.unmount();
instanceRef.current = null;
};
}
}, []);
instance.setPayload(payload ?? {});
return instance;
}