Skip to content
This repository has been archived by the owner on Feb 14, 2023. It is now read-only.

Commit

Permalink
resolves #78
Browse files Browse the repository at this point in the history
resolves #66
  • Loading branch information
quisido committed Jun 1, 2019
1 parent 8cbede4 commit ab6b746
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 118 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "reactn",
"version": "2.0.3",
"version": "2.0.4",
"author": "Charles Stover <[email protected]>",
"description": "React, but with built-in global state management.",
"homepage": "https://github.com/CharlesStover/reactn#readme",
Expand Down
54 changes: 10 additions & 44 deletions src/create-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,24 @@ import Callback from '../types/callback';
import Dispatcher, { ExtractArguments } from '../types/dispatcher';
import Dispatchers from '../types/dispatchers';
import NewGlobalState from '../types/new-global-state';
import ReactNProvider from '../types/provider';
import Reducer, { AdditionalReducers } from '../types/reducer';
import UseGlobal, { GlobalTuple, StateTuple } from '../types/use-global';
import WithGlobal, { Getter, Setter } from '../types/with-global';
import Context from './context';
import addReducer from './add-reducer';
import addReducers from './add-reducers';
import GlobalStateManager from './global-state-manager';
import setGlobal from './set-global';
import useDispatch, { UseDispatch } from './use-dispatch';
import useGlobal, { GlobalTuple, StateTuple, UseGlobal } from './use-global';
import useGlobal from './use-global';
import REACT_CONTEXT_ERROR from './utils/react-context-error';
import withGlobal, { Getter, Setter, WithGlobal } from './with-global';
import withGlobal from './with-global';



type BooleanFunction = () => boolean;

export interface ReactNProvider<
G extends {} = State,
R extends {} = Reducers,
> {
addCallback(callback: Callback<G>): BooleanFunction;
addReducer<A extends any[] = any[]>(
name: string,
reducer: Reducer<G, R, A>,
): BooleanFunction;
addReducers(reducers: AdditionalReducers<G, R>): BooleanFunction;
dispatch: Dispatchers<G, R>;
getDispatch(): Dispatchers<G, R>;
getGlobal(): G;
global: G;
removeCallback(callback: Callback<G>): boolean;
reset(): void;
setGlobal(
newGlobalState: NewGlobalState<G>,
callback?: Callback<G>,
): Promise<G>;
useDispatch<A extends any[] = any[]>(
reducer: Reducer<G, R, A>,
): Dispatcher<G, A>;
useDispatch<K extends keyof R = keyof R>(
reducer: K,
): Dispatcher<G, ExtractArguments<R[K]>>;
useGlobal(): GlobalTuple<G>;
useGlobal<Property extends keyof G>(
property: Property,
): StateTuple<G, Property>;
withGlobal<HP, LP>(
getter: Getter<G, HP, LP>,
setter: Setter<G, HP, LP>,
): WithGlobal<HP, LP>;
new (props: {}, context?: any): React.Component<{}, {}>;
}



export default function _createProvider<
Expand Down Expand Up @@ -156,11 +122,11 @@ export default function _createProvider<
return useGlobal(globalStateManager, property);
}

public static withGlobal<HP, LP>(
getter: Getter<G, HP, LP> = (globalState: G): G => globalState,
setter: Setter<G, HP, LP> = (): null => null,
public static withGlobal<HP extends {} = {}, LP extends {} = {}>(
getter: Getter<G, R, HP, LP> = (global: G): G => global,
setter: Setter<G, R, HP, LP> = (): null => null,
): WithGlobal<HP, LP> {
return withGlobal<G, HP, LP>(globalStateManager, getter, setter);
return withGlobal<G, R, HP, LP>(globalStateManager, getter, setter);
}

public render(): JSX.Element {
Expand All @@ -170,5 +136,5 @@ export default function _createProvider<
</Context.Provider>
);
}
}
};
}
15 changes: 9 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ import {
import Dispatcher, { ExtractArguments } from '../types/dispatcher';
import Dispatchers from '../types/dispatchers';
import NewGlobalState from '../types/new-global-state';
import ReactNProvider from '../types/provider';
import Reducer, { AdditionalReducers } from '../types/reducer';
import { GlobalTuple, StateTuple } from '../types/use-global';
import WithGlobal, { Getter, Setter } from '../types/with-global';
import { ReactNComponent, ReactNPureComponent } from './components';
import addCallback from './add-callback';
import addReducer from './add-reducer';
import addReducers from './add-reducers';
import createProvider, { ReactNProvider } from './create-provider';
import createProvider from './create-provider';
import reactn from './decorator';
import defaultGlobalStateManager from './default-global-state-manager';
import getDispatch from './get-dispatch';
Expand All @@ -22,8 +25,8 @@ import removeCallback from './remove-callback';
import resetGlobal from './reset-global';
import setGlobal from './set-global';
import useDispatch from './use-dispatch';
import useGlobal, { GlobalTuple, StateTuple } from './use-global';
import withGlobal, { Getter, Setter, WithGlobal } from './with-global';
import useGlobal from './use-global';
import withGlobal from './with-global';



Expand Down Expand Up @@ -92,9 +95,9 @@ interface ReactN extends TypeOfReact {

useGlobal<G extends {} = State>(): GlobalTuple<G>;

withGlobal<G extends {} = State, HP extends {} = {}, LP extends {} = {}>(
getter?: Getter<G, HP, LP>,
setter?: Setter<G, HP, LP>,
withGlobal<G extends {} = State, R extends {} = Reducers, HP extends {} = {}, LP extends {} = {}>(
getter?: Getter<G, R, HP, LP>,
setter?: Setter<G, R, HP, LP>,
): WithGlobal<HP, LP>;
}

Expand Down
21 changes: 1 addition & 20 deletions src/use-global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useForceUpdate from 'use-force-update';
import { State } from '../default';
import Callback from '../types/callback';
import NewGlobalState from '../types/new-global-state';
import UseGlobal, { GlobalTuple, StateTuple } from '../types/use-global';
import Context from './context';
import defaultGlobalStateManager from './default-global-state-manager';
import GlobalStateManager from './global-state-manager';
Expand All @@ -11,30 +12,10 @@ import REACT_HOOKS_ERROR from './utils/react-hooks-error';



export type GlobalTuple<GS> = [
GS,
(newGlobalState: NewGlobalState<GS>, callback?: Callback<GS>) => Promise<GS>,
];

type Setter<G extends {}, P extends keyof G> =
(newValue: G[P], callback?: Callback<G>) => Promise<G>;

export type StateTuple<G extends {}, P extends keyof G> = [
G[P],
Setter<G, P>,
];

export type UseGlobal<G extends {}, Property extends keyof G> =
GlobalTuple<G> | StateTuple<G, Property>;

type VoidFunction = () => void;







// useGlobal()
export default function _useGlobal<G extends {} = State>(
overrideGlobalStateManager: GlobalStateManager<G> | null,
Expand Down
80 changes: 37 additions & 43 deletions src/with-global.ts → src/with-global.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {
ComponentClass,
Context,
createElement,
FunctionComponent,
} from 'react';
import { State } from '../default';
import * as React from 'react';
import { Reducers, State } from '../default';
import Callback from '../types/callback';
import { ReactNComponentClass } from '../types/component-class';
import Dispatchers from '../types/dispatchers';
import WithGlobal, {
Getter,
LowerOrderComponent,
Setter,
} from '../types/with-global';
import NewGlobalState from '../types/new-global-state';
import { ReactNComponent } from './components';
import ReactNContext from './context';
Expand All @@ -16,25 +17,6 @@ import { ReactNGlobal, ReactNSetGlobal } from './methods';



export type Getter<G extends {}, HP, LP> = (globalState: G, props: HP) =>
null | Partial<LP> | void;

type LowerOrderComponent<P = {}> =
ComponentClass<P> | FunctionComponent<P> | string;

type SetGlobal<G extends {} = State> = (
newGlobalState: NewGlobalState<G>,
callback?: Callback<G>,
) => Promise<G>;

export type Setter<G, HP, LP> = (setGlobal: SetGlobal<G>, props: HP) =>
null | Partial<LP> | void;

export type WithGlobal<HP, LP> =
(Component: LowerOrderComponent<LP>) => ComponentClass<HP>;



// Get the name of a Component.
const componentName = <
P extends {} = {},
Expand Down Expand Up @@ -66,12 +48,13 @@ hoc(MyComponent);
*/
export default function _withGlobal<
G extends {} = State,
R extends {} = Reducers,
HP extends {} = {},
LP extends {} = {},
>(
globalStateManager: GlobalStateManager<G> | null = null,
getter: Getter<G, HP, LP> = (globalState: G): G => globalState,
setter: Setter<G, HP, LP> = (): null => null,
globalStateManager: GlobalStateManager<G, R> | null = null,
getter: Getter<G, R, HP, LP> = (global: G): G => global,
setter: Setter<G, R, HP, LP> = (): null => null,
): WithGlobal<HP, LP> {
return function ReactNWithGlobal(
Component: LowerOrderComponent<LP>,
Expand All @@ -83,18 +66,31 @@ export default function _withGlobal<

return class ReactNHOC extends ReactNComponent<HP, {}, G> {

// Context knows it provides a GlobalStateManager,
// but not the shape <GS> of the GlobalState that it holds.
public static contextType: Context<GlobalStateManager<G>> =
ReactNContext as Context<GlobalStateManager<G>>;
// Context knows it provides a GlobalStateManager, but not the shape.
public static contextType: React.Context<GlobalStateManager<G, R>> =
ReactNContext;

public static displayName = `${componentName(Component)}-ReactN`;

// Context knows it provides a GlobalStateManager, but not the shape.
public context: GlobalStateManager<G, R>;

public get dispatch(): Dispatchers<G, R> {
return this.globalStateManager.dispatchers;
}

public get global(): G {
return ReactNGlobal<G>(
this,
globalStateManager || this.context || defaultGlobalStateManager
);
return ReactNGlobal<G>(this, this.globalStateManager);
}

public get globalStateManager(): GlobalStateManager<G, R> {
if (globalStateManager) {
return globalStateManager;
}
if (this.context instanceof GlobalStateManager) {
return this.context;
}
return defaultGlobalStateManager as GlobalStateManager<G, R>;
}

public setGlobal = (
Expand All @@ -106,20 +102,18 @@ export default function _withGlobal<
!isComponentDidMount &&
!isComponentDidUpdate &&
!isSetGlobalCallback,
globalStateManager ||
this.context ||
defaultGlobalStateManager,
this.globalStateManager,
);

public render(): JSX.Element {

// @ts-ignore: LP doesn't match HP
const lowerOrderProps: LP = {
...this.props,
...getter(this.global, this.props),
...setter(this.setGlobal, this.props),
...getter(this.global, this.dispatch, this.props),
...setter(this.setGlobal, this.dispatch, this.props),
};
return createElement(Component, lowerOrderProps);
return <Component {...lowerOrderProps} />;
}
};
};
Expand Down
4 changes: 0 additions & 4 deletions tests/with-global.test.ts

This file was deleted.

81 changes: 81 additions & 0 deletions tests/with-global.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import * as React from 'react';
import { render } from 'react-testing-library';
import ReactN = require('../src/index');
import ReactNProvider from '../types/provider';
import { Getter } from '../types/with-global';
import { G, INITIAL_STATE } from './utils/initial';



// TS2339: Property 'innerHTML' does not exist on type 'HTMLSpanElement'.
interface Container extends HTMLSpanElement {
innerHTML: string;
}

interface Props {
z: string;
}



const INNER_HTML: string = `<span>${INITIAL_STATE.z}</span>`;

const mapGlobalToProps: Getter<G, {}, {}, Props> = ({ z }: G): Props => ({ z });

const TestComponent = ({ z }: Props): JSX.Element => {
return <span>{z}</span>;
};



describe('withGlobal', (): void => {

afterEach((): void => {
ReactN.resetGlobal();
});

it('should support default global state', async (): Promise<void> => {
await ReactN.setGlobal(INITIAL_STATE);
const TestComponentHoc: React.ComponentClass<{}> =
ReactN.withGlobal(mapGlobalToProps)(TestComponent);

const testComponent = render(<TestComponentHoc />);

const span: Container = testComponent.container as Container;
expect(span.innerHTML).toBe(INNER_HTML);
});

describe('Provider', (): void => {

let Provider: ReactNProvider<G>;
beforeEach((): void => {
Provider = ReactN.createProvider(INITIAL_STATE);
});

it('should support Context', (): void => {
const TestComponentHoc: React.ComponentClass<{}> =
ReactN.withGlobal(mapGlobalToProps)(TestComponent);

const testComponent = render(
<Provider>
<TestComponentHoc />
</Provider>,
);

const span: Container = testComponent.container as Container;
expect(span.innerHTML).toBe(INNER_HTML);
});

it('should support Provider', (): void => {
const TestComponentHoc: React.ComponentClass<{}> =
Provider.withGlobal(mapGlobalToProps)(TestComponent);

const testComponent = render(<TestComponentHoc />);

const span: Container = testComponent.container as Container;
expect(span.innerHTML).toBe(INNER_HTML);
});

});

});
Loading

0 comments on commit ab6b746

Please sign in to comment.