├── docs ├── src │ ├── react-app-env.d.ts │ ├── components │ │ ├── app │ │ │ ├── index.ts │ │ │ ├── header │ │ │ │ ├── index.ts │ │ │ │ ├── react-logo │ │ │ │ │ ├── index.ts │ │ │ │ │ └── app-header-react-logo.scss │ │ │ │ ├── github-banner │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── app-header-github-banner.scss │ │ │ │ │ └── app-header-github-banner.tsx │ │ │ │ ├── app-header.tsx │ │ │ │ └── app-header.scss │ │ │ ├── navigation │ │ │ │ ├── index.ts │ │ │ │ ├── list-item │ │ │ │ │ ├── index.ts │ │ │ │ │ └── app-navigation-list-item.scss │ │ │ │ ├── app-navigation.scss │ │ │ │ └── app-navigation.tsx │ │ │ ├── app.test.tsx │ │ │ ├── app.scss │ │ │ └── app.tsx │ │ └── routes │ │ │ ├── about │ │ │ ├── index.ts │ │ │ ├── about.scss │ │ │ └── about.tsx │ │ │ ├── install │ │ │ ├── index.ts │ │ │ └── install.tsx │ │ │ ├── support │ │ │ ├── index.ts │ │ │ └── support.tsx │ │ │ ├── with-init │ │ │ ├── index.ts │ │ │ └── with-init.tsx │ │ │ ├── add-reducer │ │ │ ├── index.ts │ │ │ ├── example.js │ │ │ └── add-reducer.tsx │ │ │ ├── get-global │ │ │ ├── index.ts │ │ │ └── get-global.tsx │ │ │ ├── set-global │ │ │ ├── index.ts │ │ │ └── set-global.tsx │ │ │ ├── use-global │ │ │ ├── index.ts │ │ │ └── use-global.tsx │ │ │ ├── with-global │ │ │ ├── index.ts │ │ │ └── with-global.tsx │ │ │ ├── add-callback │ │ │ ├── index.ts │ │ │ ├── example2.js │ │ │ ├── example1.js │ │ │ └── add-callback.tsx │ │ │ ├── reset-global │ │ │ ├── index.ts │ │ │ └── reset-global.tsx │ │ │ ├── use-dispatch │ │ │ ├── index.ts │ │ │ └── use-dispatch.tsx │ │ │ ├── create-provider │ │ │ ├── index.ts │ │ │ └── create-provider.tsx │ │ │ └── remove-callback │ │ │ ├── index.ts │ │ │ └── remove-callback.tsx │ ├── assets │ │ ├── images │ │ │ ├── logo.jpg │ │ │ └── github.svg │ │ └── styles │ │ │ ├── media.scss │ │ │ └── rainbowify.scss │ ├── global.d.ts │ ├── reactn.ts │ ├── index.scss │ └── index.tsx ├── .env ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── README.md ├── tsconfig.json └── package.json ├── .github └── FUNDING.yml ├── types ├── omit.d.ts ├── dispatch-function.d.ts ├── callback.d.ts ├── middleware.d.ts ├── dispatcher.d.ts ├── with-init.d.ts ├── use-global.d.ts ├── dispatchers.d.ts ├── new-global-state.d.ts ├── component-class.d.ts ├── reducer.d.ts ├── with-global.d.ts ├── component.d.ts └── provider.d.ts ├── tests ├── components │ ├── props.ts │ ├── mount.tsx │ ├── component-will-unmount.tsx │ └── component-will-update.tsx ├── utils │ ├── flush-promises.ts │ ├── react-version.ts │ ├── spy-on-global-state-manager.ts │ ├── initial.ts │ └── hook-test.tsx ├── provider │ ├── utils │ │ └── it-should-require-context.ts │ ├── use-global.test.ts │ ├── get-global.test.ts │ ├── use-dispatch.ts │ ├── get-dispatch.test.ts │ ├── reset.test.ts │ ├── remove-callback.test.ts │ ├── global.test.ts │ ├── add-callback.test.ts │ ├── dispatch.test.ts │ ├── add-reducer.test.ts │ ├── add-reducers.test.ts │ ├── with-global.test.tsx │ └── set-global.test.ts ├── decorator.test.ts ├── default-global-state-manager.test.ts ├── methods │ ├── component-will-update │ │ ├── utils │ │ │ ├── mock-component-will-update.ts │ │ │ ├── test-undefined.tsx │ │ │ ├── test-instance.tsx │ │ │ └── test-prototype.tsx │ │ └── component-will-update.TODO.tsx │ └── component-will-unmount │ │ ├── utils │ │ ├── test-undefined.tsx │ │ ├── mock-component-will-unmount.ts │ │ ├── test-instance.tsx │ │ └── test-prototype.tsx │ │ └── component-will-unmount.TODO.tsx ├── reset-global.test.ts ├── global-state-manager │ ├── clear-queue.test.ts │ ├── enqueue.test.ts │ ├── get-dispatcher.test.ts │ ├── dispatcher-map.test.ts │ ├── remove-callback.test.ts │ ├── constructor.test.ts │ ├── flush.test.ts │ ├── spy-state.test.ts │ ├── add-callback.test.ts │ ├── add-reducer.test.ts │ ├── remove-dispatcher.test.ts │ ├── remove-middleware.test.ts │ ├── add-middleware.test.ts │ ├── set-promise.test.ts │ ├── set-function.test.ts │ ├── set-object.test.ts │ ├── remove-property-listener.test.ts │ ├── add-property-listener.test.ts │ └── reset.test.ts ├── get-global.test.ts ├── object-get-listener.test.ts ├── context.test.ts ├── index.js ├── remove-callback.test.ts ├── add-reducer.test.ts ├── add-callback.test.ts ├── add-reducers.test.ts ├── get-dispatch.test.ts ├── use-global │ └── undefined-component.test.tsx ├── context │ ├── use-dispatch-string.test.ts │ ├── use-dispatch-function.test.ts │ └── use-dispatch-undefined.test.ts ├── use-dispatch │ ├── string.test.ts │ ├── undefined.test.ts │ └── function-undefined.test.ts ├── set-global.test.ts ├── create-provider.test.ts └── with-init.test.tsx ├── .gitignore ├── src ├── utils │ ├── react-hooks-error.ts │ ├── react-context-error.ts │ ├── is-property-reducer.ts │ ├── object-get-listener.ts │ ├── get-global-state-manager.ts │ ├── component-will-unmount.ts │ ├── component-will-update.ts │ ├── should-component-update.ts │ └── bind-lifecycle-methods.ts ├── default-global-state-manager.ts ├── reset-global.ts ├── get-global.ts ├── remove-callback.ts ├── get-dispatch.ts ├── add-callback.ts ├── context.ts ├── add-reducer.ts ├── add-reducers.ts ├── set-global.ts ├── with-init.tsx ├── components.ts └── use-global.ts ├── .babelrc ├── .npmignore ├── default.d.ts ├── jest.config.js ├── tsconfig.json ├── LICENSE ├── .eslintrc.js ├── package.json ├── .travis.yml └── FAQ.md /docs/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: CharlesStover 2 | -------------------------------------------------------------------------------- /docs/src/components/app/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/header/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/about/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './about'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/install/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './install'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/support/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './support'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/with-init/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './with-init'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-navigation'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-reducer/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './add-reducer'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/get-global/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './get-global'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/set-global/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './set-global'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/use-global/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './use-global'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/with-global/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './with-global'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-callback/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './add-callback'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/reset-global/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './reset-global'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/use-dispatch/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './use-dispatch'; 2 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesStover/reactn/HEAD/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/src/components/routes/create-provider/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './create-provider'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/remove-callback/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './remove-callback'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/header/react-logo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header-react-logo'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/header/github-banner/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header-github-banner'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/list-item/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-navigation-list-item'; 2 | -------------------------------------------------------------------------------- /docs/src/assets/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesStover/reactn/HEAD/docs/src/assets/images/logo.jpg -------------------------------------------------------------------------------- /types/omit.d.ts: -------------------------------------------------------------------------------- 1 | type Omit = Pick>; 2 | 3 | export default Omit; 4 | -------------------------------------------------------------------------------- /docs/src/assets/styles/media.scss: -------------------------------------------------------------------------------- 1 | @mixin mobile { 2 | @media (max-width: 480px) { 3 | @content; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/components/props.ts: -------------------------------------------------------------------------------- 1 | export default interface Props { 2 | a?: boolean; 3 | b?: number; 4 | c?: string; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /build 3 | /docs/build 4 | /docs/node_modules 5 | /jest 6 | /node_modules 7 | /package-lock.json 8 | /yarn-error.log 9 | -------------------------------------------------------------------------------- /src/utils/react-hooks-error.ts: -------------------------------------------------------------------------------- 1 | export default new Error( 2 | 'The installed version of React does not support Hooks. ' + 3 | 'Upgrade to React v16.8.0 or later.' 4 | ); 5 | -------------------------------------------------------------------------------- /src/utils/react-context-error.ts: -------------------------------------------------------------------------------- 1 | export default new Error( 2 | 'The installed version of React does not support Context. ' + 3 | 'Upgrade to React v16.3.0 or later.' 4 | ); 5 | -------------------------------------------------------------------------------- /tests/utils/flush-promises.ts: -------------------------------------------------------------------------------- 1 | export default function flushPromises(): Promise { 2 | return new Promise((resolve): void => { 3 | setImmediate(resolve); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "@babel/env", { 4 | "targets": { 5 | "node": "current" 6 | } 7 | } ], 8 | "@babel/typescript", 9 | ], 10 | } 11 | -------------------------------------------------------------------------------- /src/default-global-state-manager.ts: -------------------------------------------------------------------------------- 1 | import GlobalStateManager from './global-state-manager'; 2 | 3 | type RSA = Record; 4 | 5 | export default new GlobalStateManager(); 6 | -------------------------------------------------------------------------------- /docs/src/components/routes/support/support.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function Support(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/with-init/with-init.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function WithInit(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/get-global/get-global.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function GetGlobal(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/set-global/set-global.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function SetGlobal(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/use-global/use-global.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function UseGlobal(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/with-global/with-global.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function WithGlobal(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/reset-global/reset-global.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function ResetGlobal(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/use-dispatch/use-dispatch.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function UseDispatch(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/assets/styles/rainbowify.scss: -------------------------------------------------------------------------------- 1 | @mixin rainbowify($property) { 2 | transition-delay: 0s; 3 | transition-duration: 1s; 4 | transition-property: $property; 5 | transition-timing-function: linear; 6 | } 7 | -------------------------------------------------------------------------------- /docs/src/components/routes/create-provider/create-provider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function CreateProvider(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /docs/src/components/routes/remove-callback/remove-callback.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function RemoveCallback(): JSX.Element { 4 | return ( 5 |

Coming soon...

6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /src/reset-global.ts: -------------------------------------------------------------------------------- 1 | import GlobalStateManager from './global-state-manager'; 2 | 3 | 4 | 5 | export default function _resetGlobal( 6 | globalStateManager: GlobalStateManager, 7 | ): void { 8 | return globalStateManager.reset(); 9 | }; 10 | -------------------------------------------------------------------------------- /types/dispatch-function.d.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | import NewGlobalState from './new-global-state'; 3 | 4 | type DispatchFunction = (newGlobalState: NewGlobalState) => Promise; 5 | 6 | export default DispatchFunction; 7 | -------------------------------------------------------------------------------- /docs/src/components/app/header/react-logo/app-header-react-logo.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../assets/styles/rainbowify.scss"; 2 | 3 | .app-header-react-logo { 4 | 5 | > g { 6 | > circle, 7 | > path, 8 | > polygon { 9 | @include rainbowify(fill); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /docs/src/components/routes/install/install.tsx: -------------------------------------------------------------------------------- 1 | import React, { useDispatch } from 'reactn'; 2 | 3 | export default function Install(): JSX.Element { 4 | const setColor = useDispatch('setColor'); 5 | setColor('#FF0000'); 6 | return <> 7 | yarn add reactn 8 | ; 9 | } 10 | -------------------------------------------------------------------------------- /src/get-global.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | import GlobalStateManager from './global-state-manager'; 3 | 4 | 5 | 6 | export default function _getGlobal( 7 | globalStateManager: GlobalStateManager, 8 | ): G { 9 | return globalStateManager.state; 10 | }; 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.git 2 | /.vscode 3 | /docs 4 | /jest 5 | /node_modules 6 | /src 7 | /tests 8 | /.babelrc 9 | /.eslintrc.js 10 | /.gitignore 11 | /.npmignore 12 | /.travis.yml 13 | /FAQ.md 14 | /jest.config.js 15 | /package-lock.json 16 | /Provider.md 17 | /tsconfig.json 18 | /yarn.lock 19 | /yarn-error.log 20 | -------------------------------------------------------------------------------- /default.d.ts: -------------------------------------------------------------------------------- 1 | import DispatchFunction from './types/dispatch-function'; 2 | import Dispatchers from './types/dispatchers'; 3 | 4 | type DispatcherMap = DispatchFunction & Dispatchers; 5 | 6 | export interface Dispatch extends DispatcherMap { } 7 | 8 | export interface Reducers { } 9 | 10 | export interface State { } 11 | -------------------------------------------------------------------------------- /docs/src/global.d.ts: -------------------------------------------------------------------------------- 1 | import 'reactn'; 2 | 3 | declare module 'reactn/default' { 4 | 5 | export interface Reducers { 6 | setColor: ( 7 | global: State, 8 | dispatch: Dispatch, 9 | color: string, 10 | ) => Pick; 11 | } 12 | 13 | export interface State { 14 | color: string; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /docs/src/reactn.ts: -------------------------------------------------------------------------------- 1 | import { addReducer, setGlobal } from 'reactn'; 2 | import { State } from 'reactn/default'; 3 | 4 | const INITIAL_STATE: State = { 5 | color: '#61DAFB', 6 | }; 7 | 8 | setGlobal(INITIAL_STATE); 9 | 10 | addReducer('setColor', (global, _dispatch, color) => { 11 | if (color !== global.color) { 12 | return { color }; 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /tests/components/mount.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import React, { ComponentClass } from '../../build'; 3 | import { G, R } from '../utils/initial'; 4 | 5 | 6 | 7 | export default (TestComponent: ComponentClass<{}, {}, G, R>): void => { 8 | 9 | it('should mount without error', (): void => { 10 | render(); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /docs/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#202020", 3 | "display": "standalone", 4 | "icons": [ 5 | { 6 | "sizes": "64x64 32x32 24x24 16x16", 7 | "src": "favicon.ico", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "name": "ReactN Documentation", 12 | "short_name": "ReactN Docs", 13 | "start_url": ".", 14 | "theme_color": "#202020" 15 | } 16 | -------------------------------------------------------------------------------- /src/remove-callback.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | import Callback from '../types/callback'; 3 | import GlobalStateManager from './global-state-manager'; 4 | 5 | 6 | 7 | export default function _removeCallback( 8 | globalStateManager: GlobalStateManager, 9 | callback: Callback, 10 | ): boolean { 11 | return globalStateManager.removeCallback(callback); 12 | }; 13 | -------------------------------------------------------------------------------- /tests/provider/utils/it-should-require-context.ts: -------------------------------------------------------------------------------- 1 | import createProvider from '../../../src/create-provider'; 2 | import REACT_CONTEXT_ERROR from '../../../src/utils/react-context-error'; 3 | 4 | export default function itShouldRequireContext(): void { 5 | it('should require Context', (): void => { 6 | expect((): void => { 7 | createProvider(); 8 | }).toThrowError(REACT_CONTEXT_ERROR); 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/get-dispatch.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../default'; 2 | import Dispatchers from '../types/dispatchers'; 3 | import GlobalStateManager from './global-state-manager'; 4 | 5 | 6 | 7 | export default function _getDispatch< 8 | G extends {} = State, 9 | R extends {} = Reducers, 10 | >( 11 | globalStateManager: GlobalStateManager, 12 | ): Dispatchers { 13 | return globalStateManager.dispatchers; 14 | }; 15 | -------------------------------------------------------------------------------- /types/callback.d.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../default'; 2 | import Dispatchers from './dispatchers'; 3 | import NewGlobalState from './new-global-state'; 4 | 5 | type Callback = ( 6 | globalState: G, 7 | dispatch: Dispatchers, 8 | stateChange: Partial, 9 | reducerName?: string, 10 | reducerArgs?: any[], 11 | ) => NewGlobalState; 12 | 13 | export default Callback; 14 | -------------------------------------------------------------------------------- /docs/src/index.scss: -------------------------------------------------------------------------------- 1 | body { 2 | -moz-osx-font-smoothing: grayscale; 3 | -webkit-font-smoothing: antialiased; 4 | background-color: #202020; 5 | color: #F0F0F0; 6 | display: flex; 7 | font-family: "Roboto", sans-serif; 8 | height: 100%; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | html { 14 | font-size: 16px; 15 | height: 100%; 16 | } 17 | 18 | #root { 19 | display: flex; 20 | flex-grow: 1; 21 | overflow: auto; 22 | } 23 | -------------------------------------------------------------------------------- /docs/src/components/app/header/github-banner/app-header-github-banner.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../assets/styles/rainbowify.scss"; 2 | 3 | .app-header-github-banner { 4 | position: absolute; 5 | right: 0; 6 | top: 0; 7 | user-select: none; 8 | } 9 | 10 | .app-header-github-banner-arm { 11 | -webkit-transform-origin: 130px 106px; 12 | transform-origin: 130px 106px 13 | } 14 | 15 | .app-header-github-banner-background { 16 | @include rainbowify(fill); 17 | } 18 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-callback/example2.js: -------------------------------------------------------------------------------- 1 | import { addCallback, setGlobal } from 'reactn'; 2 | 3 | const removeAlert = addCallback(global => { 4 | alert(global.value); 5 | }); 6 | 7 | // The callback causes an alert on global state change: 8 | setGlobal({ value: 1 }); // 1 9 | setGlobal({ value: 2 }); // 2 10 | 11 | // No longer execute the callback. 12 | removeAlert(); 13 | 14 | // No alerts: 15 | setGlobal({ value: 3 }); 16 | setGlobal({ value: 4 }); 17 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | cacheDirectory: './jest/cache', 3 | collectCoverage: false, 4 | collectCoverageFrom: ['src/**/*'], 5 | coverageDirectory: './jest/coverage', 6 | preset: 'ts-jest', 7 | resetMocks: true, 8 | resetModules: true, 9 | restoreMocks: true, 10 | roots: ['/tests'], 11 | setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'], 12 | testRegex: '/tests/.+\\.test\\.tsx?$', 13 | verbose: false, 14 | }; 15 | -------------------------------------------------------------------------------- /src/add-callback.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | import Callback from '../types/callback'; 3 | import GlobalStateManager from './global-state-manager'; 4 | 5 | 6 | 7 | type BooleanFunction = () => boolean; 8 | 9 | 10 | 11 | export default function _addCallback( 12 | globalStateManager: GlobalStateManager, 13 | callback: Callback, 14 | ): BooleanFunction { 15 | return globalStateManager.addCallback(callback); 16 | }; 17 | -------------------------------------------------------------------------------- /docs/src/components/app/app.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | import '../../reactn'; 5 | import App from './app'; 6 | 7 | it('renders without crashing', () => { 8 | const div = document.createElement('div'); 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | div, 14 | ); 15 | ReactDOM.unmountComponentAtNode(div); 16 | }); 17 | -------------------------------------------------------------------------------- /types/middleware.d.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../default'; 2 | import Dispatchers from './dispatchers'; 3 | 4 | // Also defined in src/index.ts 5 | type Middleware< 6 | G extends {} = State, 7 | R extends {} = Reducers, 8 | > = (state: G, dispatch: Dispatchers) => G; 9 | 10 | export type MiddlewareCreator< 11 | G extends {} = State, 12 | R extends {} = Reducers, 13 | > = (state: G, dispatch: Dispatchers) => Middleware; 14 | 15 | export default Middleware; 16 | -------------------------------------------------------------------------------- /tests/utils/react-version.ts: -------------------------------------------------------------------------------- 1 | import { version } from 'react/package.json'; 2 | 3 | type SemVer = [number, number, number]; 4 | 5 | const semVer: SemVer = version 6 | .split('.') 7 | .map((i: string): number => parseInt(i, 10)) as SemVer; 8 | 9 | const [major, minor] = semVer; 10 | 11 | export const hasAsyncAct = major > 16 || (major === 16 && minor >= 9); 12 | 13 | export const hasContext = major > 16 || (major === 16 && minor >= 3); 14 | 15 | export const hasHooks = major > 16 || (major === 16 && minor >= 8); 16 | -------------------------------------------------------------------------------- /tests/decorator.test.ts: -------------------------------------------------------------------------------- 1 | import decorator from '../build/index'; 2 | import commonjs = require('../build/index'); 3 | 4 | describe('@reactn decorator', (): void => { 5 | 6 | it('should be the CommonJS export and the default export', (): void => { 7 | expect(commonjs).toBe(decorator); 8 | }); 9 | 10 | it('should be a function with 1 argument', (): void => { 11 | expect(decorator).toBeInstanceOf(Function); 12 | expect(decorator).toHaveLength(1); 13 | }); 14 | 15 | it.skip('should do more', (): void => { 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "declaration": true, 5 | "downlevelIteration": true, 6 | "esModuleInterop": false, 7 | "experimentalDecorators": true, 8 | "jsx": "react", 9 | "lib": [ "dom", "es2015", "es2017.object" ], 10 | "module": "commonjs", 11 | "outDir": "./build", 12 | "removeComments": true, 13 | "resolveJsonModule": true, 14 | "strictBindCallApply": true, 15 | "target": "es5" 16 | }, 17 | "files": [ 18 | "./src/index.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /docs/src/components/routes/about/about.scss: -------------------------------------------------------------------------------- 1 | @import "../../../assets/styles/rainbowify.scss"; 2 | 3 | .about-blockquote { 4 | @include rainbowify(color); 5 | filter: brightness(200%); 6 | font-size: 1.25rem; 7 | 8 | &:after { 9 | content: "”"; 10 | font-size: 2rem; 11 | line-height: 1rem; 12 | padding-left: 0.25rem; 13 | vertical-align: text-bottom; 14 | } 15 | 16 | &:before { 17 | content: "“"; 18 | font-size: 2rem; 19 | line-height: 1rem; 20 | padding-right: 0.25rem; 21 | vertical-align: text-bottom; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-callback/example1.js: -------------------------------------------------------------------------------- 1 | import { addCallback, setGlobal } from 'reactn'; 2 | 3 | // Every time the global state changes, this function will execute. 4 | addCallback(global => { 5 | alert(`The new value is ${global.value}!`); 6 | 7 | // If the global state was changed to 1, change it to 2. 8 | if (global.value === 1) { 9 | return { value: 2 }; 10 | } 11 | 12 | // If the global state is anything other than 1, don't change it. 13 | return null; 14 | }); 15 | 16 | setGlobal({ value: 1 }); 17 | // The new value is 1! 18 | // The new value is 2! 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # ReactN Documentation 2 | 3 | ## Install 4 | 5 | The ReactN documentation relies on a built version of the ReactN package in the 6 | root directory in addition to its own dependencies. 7 | 8 | **From the root repository directory:** 9 | 10 | * `yarn` to install the dependencies of ReactN. 11 | * `yarn build` to build the latest version of ReactN. 12 | 13 | **From the `/docs` directory:** 14 | 15 | * `yarn` to install the dependencies of the documentation. 16 | 17 | ## Run 18 | 19 | **From the `/docs` directory:** 20 | 21 | * `yarn start` to run a development instance. 22 | -------------------------------------------------------------------------------- /types/dispatcher.d.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | import Reducer from './reducer'; 3 | 4 | export default interface Dispatcher< 5 | G extends {} = State, 6 | A extends any[] = any[], 7 | > extends CallableFunction { 8 | (...args: A): Promise; 9 | } 10 | 11 | export type ExtractArguments = 12 | R extends Reducer 13 | ? A 14 | : never; 15 | 16 | export type PropertyDispatcher< 17 | G extends {} = State, 18 | P extends keyof G = keyof G, 19 | A extends any[] = any[], 20 | > = Dispatcher & [ G[P], Dispatcher ]; 21 | -------------------------------------------------------------------------------- /types/with-init.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentClass, ComponentType } from 'react'; 2 | import { Reducers, State } from '../default'; 3 | import { ReactNComponentClass } from './component-class'; 4 | 5 | export interface WithInitState { 6 | global: boolean; 7 | reducers: boolean; 8 | } 9 | 10 | type WithInit< 11 | P extends {} = {}, 12 | G extends {} = State, 13 | R extends {} = Reducers 14 | > = ( 15 | Component: ComponentType

| string, 16 | FallbackComponent?: ComponentType

| null | string, 17 | ) => ReactNComponentClass; 18 | 19 | export default WithInit; 20 | -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "isolatedModules": true, 8 | "jsx": "preserve", 9 | "lib": [ 10 | "dom", 11 | "dom.iterable", 12 | "esnext" 13 | ], 14 | "module": "esnext", 15 | "moduleResolution": "node", 16 | "noEmit": true, 17 | "resolveJsonModule": true, 18 | "skipLibCheck": true, 19 | "strict": true, 20 | "target": "es5" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | import { Context, createContext } from 'react'; 2 | import defaultGlobalStateManager from './default-global-state-manager'; 3 | import GlobalStateManager from './global-state-manager'; 4 | 5 | type AnyGSM = GlobalStateManager; 6 | 7 | interface TrueContext extends Context { 8 | _currentValue: T; 9 | _currentValue2: T; 10 | } 11 | 12 | const getContext = (): TrueContext => { 13 | if (typeof createContext === 'function') { 14 | return createContext(defaultGlobalStateManager) as TrueContext; 15 | } 16 | return null; 17 | }; 18 | 19 | export default getContext(); 20 | -------------------------------------------------------------------------------- /src/utils/is-property-reducer.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../../default'; 2 | import Reducer, { PropertyReducer } from '../../types/reducer'; 3 | 4 | // Used to accurately identify the reducer as a property reducer if a property 5 | // is specified. 6 | export default function isPropertyReducer< 7 | G extends {} = State, 8 | R extends {} = Reducers, 9 | P extends keyof G = keyof G, 10 | A extends any[] = any[], 11 | >( 12 | _reducer: Reducer | PropertyReducer, 13 | property?: keyof G, 14 | ): _reducer is PropertyReducer { 15 | return typeof property !== 'undefined'; 16 | }; 17 | -------------------------------------------------------------------------------- /types/use-global.d.ts: -------------------------------------------------------------------------------- 1 | import Callback from './callback'; 2 | import NewGlobalState from './new-global-state'; 3 | 4 | export type GlobalTuple = [ 5 | GS, 6 | (newGlobalState: NewGlobalState, callback?: Callback) => Promise, 7 | ]; 8 | 9 | export type Setter = 10 | (newValue: G[P], callback?: Callback) => Promise; 11 | 12 | export type StateTuple = [ 13 | G[P], 14 | Setter, 15 | ]; 16 | 17 | type UseGlobal = 18 | GlobalTuple | StateTuple; 19 | 20 | export default UseGlobal; 21 | -------------------------------------------------------------------------------- /docs/src/components/app/app.scss: -------------------------------------------------------------------------------- 1 | @import "../../assets/styles/media.scss"; 2 | 3 | .app { 4 | align-items: center; 5 | display: flex; 6 | flex-direction: column; 7 | flex-grow: 1; 8 | justify-content: flex-start; 9 | margin: 0 auto; 10 | max-width: 66rem; 11 | width: 100%; 12 | 13 | > div { 14 | display: flex; 15 | flex-direction: row; 16 | width: 100%; 17 | 18 | @include mobile { 19 | flex-direction: column; 20 | flex-flow: column-reverse; 21 | } 22 | 23 | > main { 24 | flex-basis: 0; 25 | flex-grow: 2; 26 | line-height: 1.5em; 27 | padding: 1em; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/assets/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/utils/object-get-listener.ts: -------------------------------------------------------------------------------- 1 | // Return an object that executes a read listener. 2 | 3 | export default function objectGetListener( 4 | obj: Shape, 5 | listener: Function, 6 | ): Shape { 7 | return (Object.keys(obj) as (keyof Shape)[]).reduce( 8 | (accumulator: Partial, key: keyof Shape): Partial => { 9 | Object.defineProperty(accumulator, key, { 10 | configurable: false, 11 | enumerable: true, 12 | get: (): Shape[keyof Shape] => { 13 | listener(key); 14 | return obj[key]; 15 | } 16 | }); 17 | return accumulator; 18 | }, 19 | Object.create(null) 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/get-global-state-manager.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../../default'; 2 | import Context from '../context'; 3 | import defaultGlobalStateManager from '../default-global-state-manager'; 4 | import GlobalStateManager from '../global-state-manager'; 5 | 6 | export default function getGlobalStateManager< 7 | G extends {} = State, 8 | R extends {} = Reducers, 9 | >(): GlobalStateManager { 10 | return ( 11 | Context && 12 | ( 13 | (Context._currentValue2 as GlobalStateManager) || 14 | (Context._currentValue as GlobalStateManager) 15 | ) 16 | ) || 17 | (defaultGlobalStateManager as GlobalStateManager); 18 | } 19 | -------------------------------------------------------------------------------- /docs/src/index.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | import { BrowserRouter } from 'react-router-dom'; 3 | import React from 'reactn'; 4 | import addReactNDevTools from 'reactn-devtools'; 5 | import App from './components/app'; 6 | import './index.scss'; 7 | import './reactn'; 8 | 9 | type TypeOfModule = typeof module; 10 | 11 | interface HotNodeModule extends TypeOfModule { 12 | hot?: { 13 | accept(): void; 14 | } 15 | } 16 | 17 | addReactNDevTools(); 18 | 19 | ReactDOM.render( 20 | 21 | 22 | , 23 | document.getElementById('root'), 24 | ); 25 | 26 | const m: HotNodeModule = module; 27 | if (m.hot) { 28 | m.hot.accept(); 29 | } 30 | -------------------------------------------------------------------------------- /types/dispatchers.d.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../default'; 2 | import Dispatcher, { ExtractArguments } from './dispatcher'; 3 | 4 | // Additional Dispatchers cannot maintain their argument types, as they don't 5 | // exist until runtime. 6 | export interface AdditionalDispatchers { 7 | [name: string]: Dispatcher; 8 | } 9 | 10 | export type DispatcherMap = { 11 | [name in keyof R]: Dispatcher>; 12 | }; 13 | 14 | type Dispatchers< 15 | G extends {} = State, 16 | R extends {} = Reducers, 17 | > = DispatcherMap & AdditionalDispatchers; 18 | 19 | export default Dispatchers; 20 | -------------------------------------------------------------------------------- /docs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 16 | ReactN Documentation 17 | 18 | 19 | 22 |

23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/default-global-state-manager.test.ts: -------------------------------------------------------------------------------- 1 | import defaultGlobalStateManager from '../src/default-global-state-manager'; 2 | import GlobalStateManager from '../src/global-state-manager'; 3 | 4 | 5 | 6 | const EMPTY_OBJECT: {} = Object.create(null); 7 | 8 | 9 | 10 | describe('Default GlobalStateManager', (): void => { 11 | 12 | it('should be a GlobalStateManager', (): void => { 13 | expect(defaultGlobalStateManager).toBeInstanceOf(GlobalStateManager); 14 | }); 15 | 16 | it('should have an empty state', (): void => { 17 | expect(defaultGlobalStateManager.state).toStrictEqual(EMPTY_OBJECT); 18 | }); 19 | 20 | it('should not have dispatchers', (): void => { 21 | expect(defaultGlobalStateManager.dispatchers).toStrictEqual(EMPTY_OBJECT); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /types/new-global-state.d.ts: -------------------------------------------------------------------------------- 1 | import { State } from '../default'; 2 | 3 | // AsynchronousNewGlobalState is an interface so that NewGlobalState does not 4 | // circularly reference itself. 5 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 6 | interface AsynchronousNewGlobalState 7 | extends Promise> { } 8 | 9 | export interface FunctionalNewGlobalState { 10 | (global: G, reducerName?: string, reducerArgs?: any[]): NewGlobalState; 11 | } 12 | 13 | type NewGlobalState = 14 | AsynchronousNewGlobalState | 15 | FunctionalNewGlobalState | 16 | SynchronousNewGlobalState; 17 | 18 | export default NewGlobalState; 19 | 20 | type SynchronousNewGlobalState = 21 | null | Partial | void; 22 | -------------------------------------------------------------------------------- /tests/methods/component-will-update/utils/mock-component-will-update.ts: -------------------------------------------------------------------------------- 1 | import * as methods from '../../../../src/methods'; 2 | import * as componentWillUpdate from '../../../../src/utils/component-will-update'; 3 | 4 | 5 | 6 | jest.mock('../../../../src/methods', () => ({ 7 | ...methods, 8 | ReactNComponentWillUpdate: jest.fn(methods.ReactNComponentWillUpdate), 9 | })); 10 | 11 | jest.mock('../../../../src/utils/component-will-update', () => ({ 12 | ...componentWillUpdate, 13 | componentWillUpdateInstance: 14 | jest.fn(componentWillUpdate.componentWillUpdateInstance), 15 | componentWillUpdatePrototype: 16 | jest.fn(componentWillUpdate.componentWillUpdatePrototype), 17 | })); 18 | 19 | export default function unmock() { 20 | jest.unmock('../../../../src/methods'); 21 | jest.unmock('../../../../src/utils/component-will-update'); 22 | } 23 | -------------------------------------------------------------------------------- /types/component-class.d.ts: -------------------------------------------------------------------------------- 1 | import { ComponentClass } from 'react'; 2 | import { Reducers, State } from '../default'; 3 | import { ReactNComponent, ReactNPureComponent } from './component'; 4 | import Omit from './omit'; 5 | 6 | export interface ReactNComponentClass< 7 | P extends {} = {}, 8 | S extends {} = {}, 9 | G extends {} = State, 10 | R extends {} = Reducers, 11 | SS = any, 12 | > extends Omit, 'constructor' | 'new'> { 13 | new (props: P, context?: any): ReactNComponent; 14 | } 15 | 16 | export interface ReactNPureComponentClass< 17 | P extends {} = {}, 18 | S extends {} = {}, 19 | G extends {} = State, 20 | R extends {} = Reducers, 21 | SS = any, 22 | > extends Omit, 'constructor' | 'new'> { 23 | new (props: P, context?: any): ReactNPureComponent; 24 | } 25 | -------------------------------------------------------------------------------- /tests/methods/component-will-unmount/utils/test-undefined.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import * as React from 'react'; 3 | import { ReactNComponent } from '../../../../src/components'; 4 | import { ReactNComponentWillUnmount } from '../../../../src/methods'; 5 | 6 | 7 | 8 | type VoidFunction = () => void; 9 | 10 | 11 | 12 | export default function testUndefined( 13 | Component: typeof ReactNComponent, 14 | ): VoidFunction { 15 | return (): void => { 16 | 17 | class TestComponent extends Component { 18 | render() { 19 | return null; 20 | } 21 | } 22 | 23 | const testComponent = render() 24 | testComponent.unmount(); 25 | 26 | expect(ReactNComponentWillUnmount).toHaveBeenCalledTimes(1); 27 | // expect(ReactNComponentWillUnmount).toHaveBeenCalledWith(); 28 | }; 29 | }; 30 | -------------------------------------------------------------------------------- /tests/methods/component-will-unmount/utils/mock-component-will-unmount.ts: -------------------------------------------------------------------------------- 1 | import * as methods from '../../../../src/methods'; 2 | import * as componentWillUnmount from '../../../../src/utils/component-will-unmount'; 3 | 4 | 5 | 6 | jest.mock('../../../../src/methods', () => ({ 7 | ...methods, 8 | ReactNComponentWillUnmount: jest.fn(methods.ReactNComponentWillUnmount), 9 | })); 10 | 11 | jest.mock('../../../../src/utils/component-will-unmount', () => ({ 12 | ...componentWillUnmount, 13 | componentWillUnmountInstance: 14 | jest.fn(componentWillUnmount.componentWillUnmountInstance), 15 | componentWillUnmountPrototype: 16 | jest.fn(componentWillUnmount.componentWillUnmountPrototype), 17 | })); 18 | 19 | export default function unmock() { 20 | jest.unmock('../../../../src/methods'); 21 | jest.unmock('../../../../src/utils/component-will-unmount'); 22 | } 23 | -------------------------------------------------------------------------------- /docs/src/components/app/header/app-header.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | import React, { useGlobal } from 'reactn'; 3 | import './app-header.scss'; 4 | import GitHubBanner from './github-banner'; 5 | import ReactLogo from './react-logo'; 6 | 7 | 8 | 9 | export default function AppHeader() { 10 | const [ color ] = useGlobal('color'); 11 | return ( 12 |
18 |
19 |

20 | 24 | React 25 | 29 | N 30 | 31 |

32 |
33 | 34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /types/reducer.d.ts: -------------------------------------------------------------------------------- 1 | import { Reducers, State } from '../default'; 2 | import DispatchFunction from './dispatch-function'; 3 | import Dispatchers from './dispatchers'; 4 | import NewGlobalState from './new-global-state'; 5 | 6 | export interface AdditionalReducers< 7 | G extends {} = State, 8 | R extends {} = Reducers, 9 | > { 10 | [name: string]: Reducer>; 11 | } 12 | 13 | export interface PropertyReducer< 14 | G extends {} = State, 15 | P extends keyof G = keyof G, 16 | A extends any[] = any[], 17 | > extends CallableFunction { 18 | (value: G[P], ...args: A): G[P]; 19 | } 20 | 21 | 22 | export default interface Reducer< 23 | G extends {} = State, 24 | R extends {} = Reducers, 25 | A extends any[] = any[], 26 | N extends NewGlobalState = NewGlobalState, 27 | > extends CallableFunction { 28 | (global: G, dispatch: DispatchFunction & Dispatchers, ...args: A): N; 29 | } 30 | -------------------------------------------------------------------------------- /tests/methods/component-will-update/utils/test-undefined.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import * as React from 'react'; 3 | import { ReactNComponent } from '../../../../src/components'; 4 | import { ReactNComponentWillUpdate } from '../../../../src/methods'; 5 | 6 | 7 | 8 | interface P { 9 | n: number; 10 | } 11 | 12 | type VoidFunction = () => void; 13 | 14 | 15 | 16 | export default function testUndefined( 17 | Component: typeof ReactNComponent, 18 | ): VoidFunction { 19 | return (): void => { 20 | 21 | class TestComponent extends Component

{ 22 | render() { 23 | return null; 24 | } 25 | } 26 | 27 | const testComponent = render() 28 | testComponent.rerender(); 29 | 30 | expect(ReactNComponentWillUpdate).toHaveBeenCalledTimes(1); 31 | // expect(ReactNComponentWillUpdate).toHaveBeenCalledWith(); 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-callback/add-callback.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function AddCallback(): JSX.Element { 4 | return ( 5 | <> 6 |

7 | Use addCallback to execute a function whenever the state 8 | changes. The return value of the callback will update the global state, 9 | so be sure to only return undefined or{' '} 10 | null if you do not want the global state to change. Be 11 | aware that always returning a new state value will result in an 12 | infinite loop, as the new global state will trigger the very same 13 | callback. 14 |

15 |

The only parameter is the callback function.

16 | EXAMPLE1 17 |

18 | The return value of addCallback is a function that, when 19 | executed, removes the callback. 20 |

21 | EXAMPLE2 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tests/reset-global.test.ts: -------------------------------------------------------------------------------- 1 | import GlobalStateManager from '../src/global-state-manager'; 2 | import resetGlobal from '../src/reset-global'; 3 | import spyOn from './utils/spy-on-global-state-manager'; 4 | 5 | describe('resetGlobal', (): void => { 6 | 7 | let globalStateManager: GlobalStateManager; 8 | const spy = spyOn('reset'); 9 | beforeEach((): void => { 10 | globalStateManager = new GlobalStateManager(); 11 | }); 12 | 13 | 14 | 15 | it('should be a function with 1 argument', (): void => { 16 | expect(resetGlobal).toBeInstanceOf(Function); 17 | expect(resetGlobal).toHaveLength(1); 18 | }); 19 | 20 | it('should call GlobalStateManager.reset', (): void => { 21 | resetGlobal(globalStateManager); 22 | expect(spy.reset).toHaveBeenCalledTimes(1); 23 | expect(spy.reset).toHaveBeenCalledWith(); 24 | }); 25 | 26 | it('should return undefined', (): void => { 27 | expect(resetGlobal(globalStateManager)).toBeUndefined(); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/app-navigation.scss: -------------------------------------------------------------------------------- 1 | @import "../../../assets/styles/media.scss"; 2 | @import "../../../assets/styles/rainbowify.scss"; 3 | 4 | .app-navigation { 5 | @include rainbowify(background-color); 6 | 7 | background-image: 8 | linear-gradient(0deg, #202020, rgba(32, 32, 32, 0.333)); 9 | flex-basis: 0; 10 | flex-grow: 1; 11 | padding: 0 0 0 1px; 12 | 13 | > ul { 14 | $line-height: 2em; 15 | 16 | background-color: #202020; 17 | line-height: $line-height; 18 | list-style-type: none; 19 | margin: 0; 20 | padding: ($line-height - 1em) / 2 1em; 21 | } 22 | 23 | @include mobile { 24 | background-image: 25 | linear-gradient(90deg, #202020, transparent, #202020); 26 | padding: 0 0 1px 0; 27 | 28 | > ul { 29 | display: flex; 30 | flex-direction: row; 31 | // Relative position allows sublinks to stretch to the left and right of 32 | // the parent list. 33 | position: relative; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-reducer/example.js: -------------------------------------------------------------------------------- 1 | import React, { addReducer, setGlobal, useGlobal } from 'reactn'; 2 | 3 | // Initialize the global state with the value 0. 4 | setGlobal({ value: 0 }); 5 | 6 | // When the increment reducer is called, increment the global value by X. 7 | addReducer('increment', (global, x = 1) => ({ 8 | value: global.value + x 9 | })); 10 | 11 | export default function MyComponent() { 12 | const increment = useGlobal('increment'); 13 | const [ value ] = useGlobal('value'); 14 | return ( 15 | <> 16 | The value is{' '} 17 |