Reputation: 5226
I'm trying to make a custom hook that gets 2 parameters:
1- a class that's actually is a mobx store
2- parameters of that class constructor
this hook is lookalike this in javascript:
/**
* @template T
* @param {new() => T} Store
* @param {any[]} initializers
* @returns {T} an instance of the Mobx store/viewModel
*/
export const useMobx = (Store, initializers) => {
const vm = useLazyRef(() => new Store(...initializers));
return vm.current;
};
I just trying to rewrite it in typescript but it gets really complicated and after a lots of searches I can make it like this:
interface ClassWithConstructor<T>{
new (name:string):T ;
}
type fnArgs<U,T extends ClassWithConstructor<U>> = ConstructorParameters<T> extends any[] ? ConstructorParameters<T> : never;
type UseMobxType = <U,T extends ClassWithConstructor<U>,>(Store: new(...args: fnArgs<U,T>) => U, initializers:fnArgs<U,T>) => U;
export const useMobx:UseMobxType = (Store, initializers) => {
const vm = useLazyRef(() => new Store(...initializers));
return vm.current;
};
it's works without any problem but usage was a little stupid:
useMobx<AppStatusListViewModel, typeof AppStatusListViewModel>(AppStatusListViewModel,["param1"]);
as you can see I have to pass 2 generic classes AppStatusListViewModel
and typeof AppStatusListViewModel
and I am wondering is there any better solution to make it easier to use for example like this:
useMobx<AppStatusListViewModel>(AppStatusListViewModel,["param1"]);
or even this:
useMobx(AppStatusListViewModel,["param1"]);
for more info useLazyRef source:
type initValFunctionType = ()=>any;
export const useLazyRef = (initValFunc:initValFunctionType) => {
const ref:React.MutableRefObject<any> = useRef(null);
if (ref.current === null) {
ref.current = initValFunc();
}
return ref;
};
and consider AppStatusListViewModel is just a simple ES6 class:
class AppStatusListViewModel{
constructor(name:string){
console.log(name);
}
}
Upvotes: 2
Views: 244
Reputation: 389
It seems you have over-complicated the type definitions for the custom hook.
The following type definitions should stand enough with proper type inferences & IDE intellisense.
import { useRef } from 'react'
/**
* @param initValFunc class instantiator function
*/
export const useLazyRef = <T>(initValFunc: () => T) => {
const ref = useRef<T | undefined>()
if (ref.current === undefined) {
ref.current = initValFunc()
}
return ref
}
type UseMobxType = <U, T>(
Store: new (...args: T[]) => U,
initializers?: T[]
) => U
export const useMobx: UseMobxType = (Store, initializers = []) => {
const vm = useLazyRef(() => new Store(...initializers))
return vm.current!
}
you can check for the result & do custom manipulations using the following sandbox:
https://codesandbox.io/s/usemobx-ww79oe
Upvotes: 1