mAAdhaTTah/brookjs

View on GitHub
packages/brookjs-silt/src/withRef.tsx

Summary

Maintainability
A
1 hr
Test Coverage
import React, { forwardRef, useCallback, useEffect } from 'react';
import Kefir, { Observable, Property } from 'kefir';
import { useCentralObservable } from './context';
import { wrapDisplayName } from './wrapDisplayName';
import { useSingleton } from './hooks';
 
// Ref-forward function has 2 params.
const isRefForwarding = <T, P>(
x: unknown,
): x is React.RefForwardingComponent<T, P> =>
typeof x === 'function' && x.length === 2;
 
const wrap = <E extends Element, P extends {}>(
WrappedComponent: React.ElementType<P> | React.ForwardRefRenderFunction<E, P>,
): React.ElementType<P> | React.ForwardRefExoticComponent<P> => {
if (isRefForwarding(WrappedComponent)) {
return forwardRef(WrappedComponent) as React.ForwardRefExoticComponent<P>;
}
 
return WrappedComponent;
};
 
export type Refback<
P extends {},
E extends Element,
R extends { type: string }
> = (
ref$: Property<E, never>,
props$: Property<P, never>,
) => Observable<R, never>;
 
const createProps$ = <P extends {}>() =>
new Kefir.Property<P, never>().setName(`props$`);
 
const createRef$ = <E extends Element>() =>
new Kefir.Property<E, never>().setName(`ref$`);
 
export const withRef$ = <
P extends {},
E extends Element,
R extends { type: string }
>(
refback: Refback<P, E, R>,
Function `withRef$` has 27 lines of code (exceeds 25 allowed). Consider refactoring.
) => (
WrappedComponent: React.ForwardRefRenderFunction<E, P> | React.ElementType<P>,
) => {
// @TODO(mAAdhaTTah) why isn't this type working?
const Target = wrap(WrappedComponent) as any;
 
const WithRef$: React.FC<P> = props => {
const props$ = useSingleton(createProps$ as () => Property<P, never>);
const ref$ = useSingleton(createRef$ as () => Property<E, never>);
const ref: React.RefCallback<E> = useCallback(
el => void (el && (ref$ as any)._emitValue(el)),
[ref$],
);
const central$ = useCentralObservable();
 
useEffect(() => {
(props$ as any)._emitValue(props);
}, [props, props$]);
 
useEffect(() => {
if (central$ == null) {
console.error(
'Used `withRef$` with no Central Observable. Needs to be wrapped in `<RootJunction>`',
);
return;
}
 
const plugged$ = refback(ref$, props$);
 
central$.plug(plugged$);
 
return () => void central$.unplug(plugged$);
}, [ref$, props$, central$]);
 
return <Target {...props} ref={ref} />;
};
 
WithRef$.displayName = wrapDisplayName(WrappedComponent, 'WithRef$');
 
return WithRef$;
};