Skip to content

Commit

Permalink
Removed dependency to hoist-inferno-statics and implemented way simpler
Browse files Browse the repository at this point in the history
hoist method to inferno-shared
  • Loading branch information
Havunen committed Sep 6, 2023
1 parent fe16c45 commit f045e75
Show file tree
Hide file tree
Showing 28 changed files with 988 additions and 458 deletions.
1 change: 0 additions & 1 deletion packages/inferno-mobx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"mobx": ">=6"
},
"dependencies": {
"hoist-non-inferno-statics": "^1.1.3",
"inferno": "8.2.2"
},
"devDependencies": {
Expand Down
5 changes: 2 additions & 3 deletions packages/inferno-mobx/src/observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import {
type VNode,
} from 'inferno';
import { EventEmitter } from './utils/EventEmitter';
import { warning } from 'inferno-shared';
import { warning, hoistStaticProperties } from 'inferno-shared';
import { isStateless } from './utils/utils';
import { VNodeFlags } from 'inferno-vnode-flags';
import hoistNonReactStatics from 'hoist-non-inferno-statics';

/**
* dev tool support
Expand Down Expand Up @@ -428,7 +427,7 @@ function createStoreInjector(grabStoresFn: Function, component, injectNames?) {
}

// Static fields from component should be visible on the generated Injector
hoistNonReactStatics(Injector, component);
hoistStaticProperties(Injector, component);

Injector.wrappedComponent = component;
Object.defineProperties(Injector, proxiedInjectorProps);
Expand Down
1 change: 0 additions & 1 deletion packages/inferno-redux/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"redux": ">=4"
},
"dependencies": {
"hoist-non-inferno-statics": "^1.1.3",
"inferno": "8.2.2"
},
"devDependencies": {
Expand Down
34 changes: 20 additions & 14 deletions packages/inferno-redux/src/components/Provider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Component, InfernoNode } from 'inferno';
import { Action, AnyAction, Store } from 'redux';
import { Component, type InfernoNode } from 'inferno';
import { type Action, type AnyAction, type Store } from 'redux';
import { warning } from '../utils/warning';

let didWarnAboutReceivingStore = false;
const warnAboutReceivingStore = () => {
const warnAboutReceivingStore = (): void => {
if (didWarnAboutReceivingStore) {
return;
}
Expand All @@ -18,7 +18,9 @@ export interface Props<A extends Action = AnyAction> {
children?: InfernoNode;
}

export class Provider<A extends Action = AnyAction> extends Component<Props<A>> {
export class Provider<A extends Action = AnyAction> extends Component<
Props<A>
> {
public static displayName = 'Provider';
private readonly store: Store<any, A>;

Expand All @@ -27,7 +29,7 @@ export class Provider<A extends Action = AnyAction> extends Component<Props<A>>
this.store = props.store;
}

public getChildContext() {
public getChildContext(): { store: Store<any, A>; storeSubscription: null } {
return { store: this.store, storeSubscription: null };
}

Expand All @@ -37,16 +39,20 @@ export class Provider<A extends Action = AnyAction> extends Component<Props<A>>
return this.props.children;
}

public componentWillReceiveProps?(nextProps: Readonly<{ children?: InfernoNode } & Props<A>>, nextContext: any): void;
public componentWillReceiveProps?(
nextProps: Readonly<{ children?: InfernoNode } & Props<A>>,
nextContext: any,
): void;
}

if (process.env.NODE_ENV !== 'production') {
Provider.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
const { store } = this;
const { store: nextStore } = nextProps;

if (store !== nextStore) {
warnAboutReceivingStore();
}
};
Provider.prototype.componentWillReceiveProps =
function componentWillReceiveProps(nextProps) {
const { store } = this;
const { store: nextStore } = nextProps;

if (store !== nextStore) {
warnAboutReceivingStore();
}
};
}
76 changes: 50 additions & 26 deletions packages/inferno-redux/src/components/connectAdvanced.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { VNodeFlags } from 'inferno-vnode-flags';
import { type Dispatch, type Store } from 'redux';
import hoistNonReactStatics from 'hoist-non-inferno-statics';
import { Component, createComponentVNode, normalizeProps } from 'inferno';
import {
Component,
createComponentVNode,
type InfernoNode,
normalizeProps,
} from 'inferno';
import { Subscription } from '../utils/Subscription';
import { hoistStaticProperties } from 'inferno-shared';

let hotReloadingVersion = 0;
const dummyState = {};
const noop = (): void => {};

const makeSelectorStateful = (sourceSelector, store) => {
const makeSelectorStateful = (
sourceSelector: (state: any, props: any) => any,
store: Store<any>,
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
) => {
// wrap the selector in an object that tracks its results between runs.
const selector = {
error: null as Error | null,
Expand All @@ -33,6 +41,8 @@ const makeSelectorStateful = (sourceSelector, store) => {
};

export interface IConnectOptions {
displayName: string;

/**
* the func used to compute this HOC's displayName from the wrapped component's displayName.
* probably overridden by wrapper functions such as connect().
Expand Down Expand Up @@ -98,6 +108,10 @@ export interface IConnectOptions {
areStatePropsEqual?: any;

areMergedPropsEqual?: any;

WrappedComponent?: any;

wrappedComponentName: string;
}

// TODO: This should be typed better. Spesifically, the output and input props should be generic.
Expand All @@ -107,13 +121,13 @@ export type SelectorFactory = (
) => (state: any, props: any) => any;

// TODO: Move
const invariant = (test: boolean, error: string) => {
const invariant = (test: boolean, error: string): void => {
if (!test) {
throw new Error(error);
}
};

function getDefaultName(name) {
function getDefaultName(name): string {
return `ConnectAdvanced(${name})`;
}

Expand All @@ -128,15 +142,17 @@ export function connectAdvanced(
withRef = false,
...connectOptions
}: Partial<IConnectOptions>,
) {
// eslint-disable-next-line @typescript-eslint/ban-types
): Function {
const subscriptionKey = storeKey + 'Subscription';
const version = hotReloadingVersion++;

const wrapWithConnect = <T extends Function>(WrappedComponent: T): T => {
// eslint-disable-next-line @typescript-eslint/ban-types
const wrapWithConnect = <T extends Function>(WrappedComponent: T): any => {
invariant(
typeof WrappedComponent === 'function',
`You must pass a component to the function returned by ` +
`connect. Instead received ${WrappedComponent}`,
`connect. Instead received ${WrappedComponent as any}`,
);

const wrappedComponentName: string =
Expand All @@ -160,6 +176,10 @@ export function connectAdvanced(
};

class Connect<P, S> extends Component<P, S> {
/* eslint-disable */
// @ts-ignore
public state: {};
/* eslint-enable */
public static displayName = displayName;
public static WrappedComponent = WrappedComponent;

Expand All @@ -181,7 +201,7 @@ export function connectAdvanced(
super(props, context);

this.version = version;
this.state = {} as S;
this.state = {};
this.renderCount = 0;
this.store = this.props[storeKey] || this.context[storeKey];
this.propsMode = Boolean(props[storeKey]);
Expand All @@ -200,7 +220,7 @@ export function connectAdvanced(
this.initSubscription();
}

public getChildContext() {
public getChildContext(): Record<string, any> {
// If this component received store from props, its subscription should be transparent
// to any descendants receiving store+subscription from context; it passes along
// subscription passed to it. Otherwise, it shadows the parent subscription, which allows
Expand All @@ -211,7 +231,7 @@ export function connectAdvanced(
};
}

public componentWillMount() {
public componentWillMount(): void {
if (!shouldHandleStateChanges || this.$SSR) {
return;
}
Expand All @@ -220,15 +240,15 @@ export function connectAdvanced(
this.selector.run(this.props);
}

public componentWillReceiveProps(nextProps) {
public componentWillReceiveProps(nextProps): void {
this.selector.run(nextProps);
}

public shouldComponentUpdate() {
public shouldComponentUpdate(): boolean {
return this.selector.shouldComponentUpdate;
}

public componentWillUnmount() {
public componentWillUnmount(): void {
if (this.subscription) {
this.subscription.tryUnsubscribe();
}
Expand All @@ -242,6 +262,7 @@ export function connectAdvanced(
this.selector.shouldComponentUpdate = false;
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
public getWrappedInstance() {
invariant(
withRef,
Expand All @@ -252,20 +273,20 @@ export function connectAdvanced(
return this.wrappedInstance;
}

private setWrappedInstance(ref) {
private setWrappedInstance(ref): void {
this.wrappedInstance = ref;
}

public initSelector() {
public initSelector(): void {
const sourceSelector = selectorFactory(
this.store!.dispatch,
selectorFactoryOptions,
);
this.selector = makeSelectorStateful(sourceSelector, this.store);
this.selector = makeSelectorStateful(sourceSelector, this.store!);
this.selector.run(this.props);
}

public initSubscription() {
public initSubscription(): void {
if (!shouldHandleStateChanges) {
return;
}
Expand All @@ -292,18 +313,18 @@ export function connectAdvanced(
);
}

private onStateChange() {
private onStateChange(): void {
this.selector.run(this.props);

if (!this.selector.shouldComponentUpdate) {
this.notifyNestedSubs!();
} else {
this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate;
this.setState(dummyState);
this.setState({});
}
}

private notifyNestedSubsOnComponentDidUpdate() {
private notifyNestedSubsOnComponentDidUpdate(): void {
// `componentDidUpdate` is conditionally implemented when `onStateChange` determines it
// needs to notify nested subs. Once called, it unimplements itself until further state
// changes occur. Doing it this way vs having a permanent `componentDidMount` that does
Expand All @@ -313,10 +334,11 @@ export function connectAdvanced(
this.notifyNestedSubs!();
}

public isSubscribed() {
public isSubscribed(): boolean {
return Boolean(this.subscription && this.subscription.isSubscribed());
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
private addExtraProps(props: any) {
if (!renderCountProp) {
return props;
Expand All @@ -326,7 +348,7 @@ export function connectAdvanced(
// this is especially important for 'ref' since that's a reference back to the component
// instance. a singleton memoized selector would then be holding a reference to the
// instance, preventing the instance from being garbage collected, and that would be bad
const withExtras = {...props};
const withExtras = { ...props };

if (renderCountProp) {
withExtras[renderCountProp] = this.renderCount++;
Expand All @@ -337,7 +359,7 @@ export function connectAdvanced(
return withExtras;
}

public render() {
public render(): InfernoNode {
const selector = this.selector;
selector.shouldComponentUpdate = false;

Expand Down Expand Up @@ -376,7 +398,9 @@ export function connectAdvanced(
};
}

return hoistNonReactStatics(Connect, WrappedComponent) as T;
hoistStaticProperties(Connect, WrappedComponent);

return Connect;
};

return wrapWithConnect;
Expand Down
4 changes: 2 additions & 2 deletions packages/inferno-redux/src/connect/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { defaultMapStateToPropsFactories } from './mapStateToProps';
import { defaultMergePropsFactories } from './mergeProps';
import { defaultSelectorFactory } from './selectorFactory';

const match = (arg, factories, name) => {
const match = (arg, factories, name): any => {
for (let i = factories.length - 1; i >= 0; i--) {
const result = factories[i](arg);
if (result) {
Expand All @@ -26,7 +26,7 @@ const match = (arg, factories, name) => {
};
};

const strictEqual = (a, b) => a === b;
const strictEqual = (a, b): boolean => a === b;

// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
Expand Down
25 changes: 19 additions & 6 deletions packages/inferno-redux/src/connect/mapDispatchToProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,27 @@ import { wrapMapToPropsConstant, wrapMapToPropsFunc } from './wrapMapToProps';

import { bindActionCreators } from 'redux';

export const whenMapDispatchToPropsIsFunction = (mapDispatchToProps) =>
typeof mapDispatchToProps === 'function' ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps') : undefined;
export const whenMapDispatchToPropsIsFunction = (
mapDispatchToProps,
): unknown =>
typeof mapDispatchToProps === 'function'
? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps')
: undefined;

export const whenMapDispatchToPropsIsMissing = (mapDispatchToProps) => (!mapDispatchToProps ? wrapMapToPropsConstant((dispatch) => ({ dispatch })) : undefined);
export const whenMapDispatchToPropsIsMissing = (mapDispatchToProps): unknown =>
!mapDispatchToProps
? wrapMapToPropsConstant((dispatch) => ({ dispatch }))
: undefined;

export const whenMapDispatchToPropsIsObject = (mapDispatchToProps) =>
export const whenMapDispatchToPropsIsObject = (mapDispatchToProps): unknown =>
mapDispatchToProps && typeof mapDispatchToProps === 'object'
? wrapMapToPropsConstant((dispatch) => bindActionCreators(mapDispatchToProps, dispatch))
? wrapMapToPropsConstant((dispatch) =>
bindActionCreators(mapDispatchToProps, dispatch),
)
: undefined;

export const defaultMapDispatchToPropsFactories = [whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject];
export const defaultMapDispatchToPropsFactories = [
whenMapDispatchToPropsIsFunction,
whenMapDispatchToPropsIsMissing,
whenMapDispatchToPropsIsObject,
];
Loading

0 comments on commit f045e75

Please sign in to comment.