├── .babelrc ├── .eslintrc.js ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── FAQ.md ├── LICENSE ├── Provider.md ├── README.md ├── default.d.ts ├── docs ├── .env ├── README.md ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json ├── src │ ├── assets │ │ ├── images │ │ │ ├── github.svg │ │ │ └── logo.jpg │ │ └── styles │ │ │ ├── media.scss │ │ │ └── rainbowify.scss │ ├── components │ │ ├── app │ │ │ ├── app.scss │ │ │ ├── app.test.tsx │ │ │ ├── app.tsx │ │ │ ├── header │ │ │ │ ├── app-header.scss │ │ │ │ ├── app-header.tsx │ │ │ │ ├── github-banner │ │ │ │ │ ├── app-header-github-banner.scss │ │ │ │ │ ├── app-header-github-banner.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── react-logo │ │ │ │ │ ├── app-header-react-logo.scss │ │ │ │ │ ├── app-header-react-logo.tsx │ │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ └── navigation │ │ │ │ ├── app-navigation.scss │ │ │ │ ├── app-navigation.tsx │ │ │ │ ├── index.ts │ │ │ │ └── list-item │ │ │ │ ├── app-navigation-list-item.scss │ │ │ │ ├── app-navigation-list-item.tsx │ │ │ │ └── index.ts │ │ └── routes │ │ │ ├── about │ │ │ ├── about.scss │ │ │ ├── about.tsx │ │ │ └── index.ts │ │ │ ├── add-callback │ │ │ ├── add-callback.tsx │ │ │ ├── example1.js │ │ │ ├── example2.js │ │ │ └── index.ts │ │ │ ├── add-reducer │ │ │ ├── add-reducer.tsx │ │ │ ├── example.js │ │ │ └── index.ts │ │ │ ├── create-provider │ │ │ ├── create-provider.tsx │ │ │ └── index.ts │ │ │ ├── get-global │ │ │ ├── get-global.tsx │ │ │ └── index.ts │ │ │ ├── install │ │ │ ├── index.ts │ │ │ └── install.tsx │ │ │ ├── remove-callback │ │ │ ├── index.ts │ │ │ └── remove-callback.tsx │ │ │ ├── reset-global │ │ │ ├── index.ts │ │ │ └── reset-global.tsx │ │ │ ├── set-global │ │ │ ├── index.ts │ │ │ └── set-global.tsx │ │ │ ├── support │ │ │ ├── index.ts │ │ │ └── support.tsx │ │ │ ├── use-dispatch │ │ │ ├── index.ts │ │ │ └── use-dispatch.tsx │ │ │ ├── use-global │ │ │ ├── index.ts │ │ │ └── use-global.tsx │ │ │ ├── with-global │ │ │ ├── index.ts │ │ │ └── with-global.tsx │ │ │ └── with-init │ │ │ ├── index.ts │ │ │ └── with-init.tsx │ ├── global.d.ts │ ├── index.scss │ ├── index.tsx │ ├── react-app-env.d.ts │ └── reactn.ts ├── tsconfig.json └── yarn.lock ├── jest.config.js ├── package.json ├── src ├── add-callback.ts ├── add-reducer.ts ├── add-reducers.ts ├── components.ts ├── context.ts ├── create-provider.tsx ├── decorator.ts ├── default-global-state-manager.ts ├── get-dispatch.ts ├── get-global.ts ├── global-state-manager.ts ├── index.ts ├── methods.ts ├── remove-callback.ts ├── reset-global.ts ├── set-global.ts ├── use-dispatch.ts ├── use-global.ts ├── utils │ ├── bind-lifecycle-methods.ts │ ├── component-will-unmount.ts │ ├── component-will-update.ts │ ├── get-global-state-manager.ts │ ├── is-property-reducer.ts │ ├── object-get-listener.ts │ ├── react-context-error.ts │ ├── react-hooks-error.ts │ └── should-component-update.ts ├── with-global.tsx └── with-init.tsx ├── tests ├── add-callback.test.ts ├── add-reducer.test.ts ├── add-reducers.test.ts ├── components │ ├── component-will-unmount.tsx │ ├── component-will-update.tsx │ ├── index.test.ts │ ├── mount.tsx │ └── props.ts ├── context.test.ts ├── context │ ├── use-dispatch-function.test.ts │ ├── use-dispatch-string.test.ts │ └── use-dispatch-undefined.test.ts ├── create-provider.test.ts ├── decorator.test.ts ├── default-global-state-manager.test.ts ├── get-dispatch.test.ts ├── get-global.test.ts ├── global-state-manager │ ├── add-callback.test.ts │ ├── add-middleware.test.ts │ ├── add-property-listener.test.ts │ ├── add-reducer.test.ts │ ├── clear-queue.test.ts │ ├── constructor.test.ts │ ├── create-dispatcher.test.ts │ ├── dispatcher-map.test.ts │ ├── enqueue.test.ts │ ├── flush.test.ts │ ├── get-dispatcher.test.ts │ ├── remove-callback.test.ts │ ├── remove-dispatcher.test.ts │ ├── remove-middleware.test.ts │ ├── remove-property-listener.test.ts │ ├── reset.test.ts │ ├── set-function.test.ts │ ├── set-object.test.ts │ ├── set-promise.test.ts │ ├── set.test.ts │ └── spy-state.test.ts ├── index.js ├── index.test.ts ├── methods │ ├── component-will-unmount │ │ ├── component-will-unmount.TODO.tsx │ │ └── utils │ │ │ ├── mock-component-will-unmount.ts │ │ │ ├── test-instance.tsx │ │ │ ├── test-prototype.tsx │ │ │ └── test-undefined.tsx │ └── component-will-update │ │ ├── component-will-update.TODO.tsx │ │ └── utils │ │ ├── mock-component-will-update.ts │ │ ├── test-instance.tsx │ │ ├── test-prototype.tsx │ │ └── test-undefined.tsx ├── object-get-listener.test.ts ├── provider │ ├── add-callback.test.ts │ ├── add-reducer.test.ts │ ├── add-reducers.test.ts │ ├── dispatch.test.ts │ ├── get-dispatch.test.ts │ ├── get-global.test.ts │ ├── global.test.ts │ ├── remove-callback.test.ts │ ├── reset.test.ts │ ├── set-global.test.ts │ ├── use-dispatch.ts │ ├── use-global.test.ts │ ├── utils │ │ └── it-should-require-context.ts │ └── with-global.test.tsx ├── remove-callback.test.ts ├── reset-global.test.ts ├── set-global.test.ts ├── use-dispatch │ ├── function-string.test.ts │ ├── function-undefined.test.ts │ ├── string.test.ts │ └── undefined.test.ts ├── use-global │ ├── string.test.ts │ ├── undefined-component.test.tsx │ └── undefined.test.ts ├── utils │ ├── flush-promises.ts │ ├── hook-test.tsx │ ├── initial.ts │ ├── react-version.ts │ └── spy-on-global-state-manager.ts ├── with-global.test.tsx └── with-init.test.tsx ├── tsconfig.json ├── types ├── callback.d.ts ├── component-class.d.ts ├── component.d.ts ├── dispatch-function.d.ts ├── dispatcher.d.ts ├── dispatchers.d.ts ├── middleware.d.ts ├── new-global-state.d.ts ├── omit.d.ts ├── provider.d.ts ├── reducer.d.ts ├── use-global.d.ts ├── with-global.d.ts └── with-init.d.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "@babel/env", { 4 | "targets": { 5 | "node": "current" 6 | } 7 | } ], 8 | "@babel/typescript", 9 | ], 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | 'plugin:@typescript-eslint/recommended', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parser: '@typescript-eslint/parser', 15 | parserOptions: { 16 | ecmaFeatures: { 17 | jsx: true, 18 | }, 19 | ecmaVersion: 2018, 20 | project: './tsconfig.json', 21 | sourceType: 'module', 22 | }, 23 | plugins: [ 24 | '@typescript-eslint', 25 | 'react', 26 | ], 27 | rules: { 28 | '@typescript-eslint/explicit-function-return-type': 'error', 29 | '@typescript-eslint/indent': [ 30 | 'warn', 2, { 31 | SwitchCase: 1, 32 | } ], 33 | '@typescript-eslint/no-empty-interface': 'off', 34 | '@typescript-eslint/no-namespace': 'off', 35 | '@typescript-eslint/no-unused-vars': [ 36 | 'error', { 37 | args: 'all', 38 | argsIgnorePattern: '^_', 39 | caughtErrors: 'all', 40 | vars: 'all', 41 | } ], 42 | indent: 'off', 43 | 'no-unused-vars': 'off', 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: CharlesStover 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 10.15.3 3 | branches: 4 | only: master 5 | cache: yarn 6 | install: 7 | - yarn 8 | - yarn build 9 | - cd docs 10 | - yarn 11 | - yarn build 12 | - cd .. 13 | script: 14 | - yarn test 15 | - cd docs 16 | - yarn test 17 | - cd .. 18 | deploy: 19 | - api_key: 20 | secure: OGUpA8E72fS8hYlpFPpOoAwRVUMxB3BgrcQWCeI8H9IpyJg3YxWOkAT0NO7EbHPhTyD81Wv03W6Wb4B02x2Wxxj3RzMKm3Vch+M1+LZBg8vHP//K5/v4NM742SnOzuBe/hZADSHQdSdn2Anawj998poXkDtPt80IlKEZ2+tEuVCZfh+DP4qHzL9hwmE2o1yon+9NXl8Bd1E4Pw2n152aNiqvk2eMJdRDMBlrH5nN/oN/UC6icLvDXTZYwTEDRXdNlR9HNqaD+J45KG6qmBVW9I69TZSz+4Y/7uiijaDKRJi+1gK4CfqhsvdWH74xJZpgxN11PZM84y3eGZQgF1JpNi4f0/JcsG2O3R1g/coyDAQFS3hk7klcNONN0h68wAocb4wCb4pg3nrPWrn7aD4YOaaDvJpwbFL1vl+WHTrJIGNSjgKbsu5/Bl2YZQzFzzv87vx6OqJmyS0uNI0kLEXCR3bwmOY4U38qih9yVlERojWKilLWK28j815L3t9lD0VGVHnYjmELQovRxr1OSLPXWMDjvRgYF2QQ7EMRegQeWmBonJkrnO3hp10uOckCLv91IYUObNLgP0g2UEwuD3zactEsjXYazNH/UD6SCI7SiaHoPTa7VIsFQpyHwjm11ROBG85gJJm8GP0Tjb1YOGLT3eZykP+fmHKaUXiwpuQlUPM= 21 | edge: true 22 | email: npmjs@charlesstover.com 23 | on: 24 | branch: master 25 | provider: npm 26 | skip_cleanup: true 27 | - edge: true 28 | github-token: 29 | secure: YHTCjBwGiVZuTCJ2NQlPLiTYN59EB82X76l7/ipy3Vv0UtnILbbxXpSEiZpN9loC3wQ8xkqNuDqu4MqL4rNKt4UvVUmHmKusrPcw16JAJdwVtRiVXqy1W+wyBgnkfhTfT7eQ5zSlJxgHZ0iv/AWzA/z3HwfnMEAJM0fFChIzJWjXJ0J/lg4lswX3XJLjpNFPjut3XCKILr9x7N9UtGdqVEJgTdvOpm6jElyJJig5Aw3n23+LE/TWCSzIbbB5xT2W6Mhc80vuXeCWRFD2Otygiu+YNICiCARV3EL2rBcIIjKfX7Vuxru42TeuRMNaF57ru1F7dA/8bGHao33ARDdrG1ynNy0DRI1QL7N5cXEaGNuOhl0+zM1r+zsaF7CpqLr/WlcD+UcqFK7YSzgPpVRzvKCU2CzRD2J8cH+HD3P9Nkapj3wvQvvP25hXcyL9Ib4OXslSDCFk1lOeGb8Y8W7X43UOHe3RMkW9sPZNdCzYxwjTjs1pdtnk6QlBLvfIJ5q4o+AvcyOYxkaZQrzZwazDNzOP2PEUQ0MsV7hZWYeSH7r3H3KQR9Ne+uh/2Uy5/3X8JIn1cgz+DP+5FhpQOhWUVg696gEdjFCWSMXDUD2+r1+1pq/bK1IiD7gj59J9FhaonWlwWqCvvk35OtebbrT6HIUhGqvyA/tGrYMYKFI+0DE= 30 | keep-history: true 31 | local-dir: docs/build 32 | on: 33 | branch: master 34 | provider: pages 35 | skip-cleanup: true 36 | target-branch: gh-pages 37 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | * [With what version of React is ReactN bundled?](#with-what-version-of-react-is-reactn-bundled) 4 | * [What version(s) of React does ReactN support?](#what-versions-of-react-does-reactn-support) 5 | * [What if my project requires a clean copy of React also?](#what-if-my-project-requires-a-clean-copy-of-react-also) 6 | * [When do I import from React and when do I import from ReactN?](#when-do-i-import-from-react-and-when-do-i-import-from-reactn) 7 | * [Is my `create-react-app` project supported?](#is-my-create-react-app-project-supported) 8 | * [How do components update?](#how-do-components-update) 9 | 10 | ## With what version of React is ReactN bundled? 11 | 12 | ReactN does _not_ come bundled with React. You must install React alongside it. 13 | ReactN piggybacks off whatever version of React that you choose to use. 14 | 15 | ## What version(s) of React does ReactN support? 16 | 17 | You may use _any_ version of React with ReactN. 18 | You may freely upgrade or downgrade to any version of React without impacting 19 | your ReactN installation. 20 | 21 | ## What if my project requires a clean copy of React also? 22 | 23 | ReactN does _not_ mutate the React package or object. It extends a copy of it. 24 | You can use React and ReactN _in the same project_, even _in the same file_! 25 | 26 | Your `react` package and its imports are completely unmodified by the use of 27 | ReactN. 28 | 29 | ``` 30 | import React from 'react'; 31 | import ReactN from 'reactn'; 32 | assert(React.Component !== ReactN.Component); 33 | ``` 34 | 35 | ## When do I import from React and when do I import from ReactN? 36 | 37 | The simplest solution is to _always import from `reactn`_, as ReactN exports 38 | the entire React package _in addition_ to global state functionality. Any time 39 | importing from React will work, importing from ReactN will also work. 40 | 41 | If your functionality exists on the React package, such as 42 | `React.createElement`, you may import that functionality from `react`. 43 | 44 | If your functionality involves global state, such as the `setGlobal` helper 45 | function, `useGlobal` hook, or ReactN versions of `Component` and 46 | `PureCompnent` (which implement the global state member variables and methods), 47 | you _must_ import them from `reactn`. 48 | 49 | ## Is my `create-react-app` project supported? 50 | 51 | Yes! ReactN supports projects bootstrapped with `create-react-app`. 52 | 53 | ## How do components update? 54 | 55 | When a component's _local_ state changes, that component "updates" or 56 | re-renders. 57 | 58 | It would not be performant to update _every_ component when the global state 59 | changes. Instead, a component only updates if a global state root property that 60 | that component has accessed has changed. 61 | 62 | If your component accesses `this.global.x`, it _will not_ re-render when 63 | `this.global.y` changes. 64 | 65 | If your component accesses `this.global.myObject.x`, it _will_ re-render when 66 | `this.global.myObject.y` changes, because the root property `myObject` has 67 | changed. You should take this into consideration when nesting objects in your 68 | global state. 69 | 70 | If you strongly desire to circumvent this behavior, you may use the 71 | `withGlobal` higher-order component to wrap a `React.memo` functional component 72 | or `PureComponent`. 73 | 74 | ```JavaScript 75 | import React, { memo, withGlobal } from 'reactn'; 76 | 77 | const Me = memo( 78 | ({ age, name }) = 79 |

My name is {name}, and I am {age}!

80 | ); 81 | 82 | export default withGlobal( 83 | (global, props) => ({ 84 | age: global.people[props.person].age, 85 | name: global.people[props.person].name 86 | }) 87 | )(Me); 88 | ``` 89 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Charles Stover 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactn-docs", 3 | "version": "2.0.0", 4 | "homepage": ".", 5 | "license": "MIT", 6 | "private": true, 7 | "browserslist": [ 8 | ">0.2%", 9 | "not dead", 10 | "not ie <= 11", 11 | "not op_mini all" 12 | ], 13 | "eslintConfig": { 14 | "extends": "react-app" 15 | }, 16 | "scripts": { 17 | "build": "react-scripts build", 18 | "eject": "react-scripts eject", 19 | "start": "react-scripts start", 20 | "test": "react-scripts test" 21 | }, 22 | "dependencies": { 23 | "react": "link:../node_modules/react", 24 | "react-dom": "link:../node_modules/react-dom", 25 | "react-router": "^5.0.0", 26 | "react-router-dom": "^5.0.0", 27 | "reactn": "link:..", 28 | "reactn-devtools": "^1.0.0", 29 | "redux": "^4.0.1", 30 | "use-react-router": "^1.0.5" 31 | }, 32 | "devDependencies": { 33 | "@types/jest": "link:../node_modules/@types/jest", 34 | "@types/node": "link:../node_modules/@types/node", 35 | "@types/react": "link:../node_modules/@types/react", 36 | "@types/react-dom": "^16.8.3", 37 | "@types/react-router": "^4.4.5", 38 | "@types/react-router-dom": "^4.3.1", 39 | "node-sass": "^4.13.1", 40 | "react-scripts": "2.1.8", 41 | "typescript": "link:../node_modules/typescript" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesStover/reactn/a648d2242ead31a282433d3237f0c89882a75258/docs/public/favicon.ico -------------------------------------------------------------------------------- /docs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 16 | ReactN Documentation 17 | 18 | 19 | 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/src/assets/images/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/src/assets/images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CharlesStover/reactn/a648d2242ead31a282433d3237f0c89882a75258/docs/src/assets/images/logo.jpg -------------------------------------------------------------------------------- /docs/src/assets/styles/media.scss: -------------------------------------------------------------------------------- 1 | @mixin mobile { 2 | @media (max-width: 480px) { 3 | @content; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /docs/src/components/app/app.tsx: -------------------------------------------------------------------------------- 1 | import { Route, Switch } from 'react-router-dom'; 2 | import React from 'reactn'; 3 | import About from '../routes/about'; 4 | import AddCallback from '../routes/add-callback'; 5 | import AddReducer from '../routes/add-reducer'; 6 | import CreateProvider from '../routes/create-provider'; 7 | import GetGlobal from '../routes/get-global'; 8 | import Install from '../routes/install'; 9 | import RemoveCallback from '../routes/remove-callback'; 10 | import ResetGlobal from '../routes/reset-global'; 11 | import SetGlobal from '../routes/set-global'; 12 | import Support from '../routes/support'; 13 | import UseDispatch from '../routes/use-dispatch'; 14 | import UseGlobal from '../routes/use-global'; 15 | import WithGlobal from '../routes/with-global'; 16 | import WithInit from '../routes/with-init'; 17 | import './app.scss'; 18 | import Header from './header'; 19 | import Navigation from './navigation'; 20 | 21 | 22 | 23 | export default function App(): JSX.Element { 24 | return ( 25 |
26 |
27 |
28 |
29 | 30 | {/* Getting Started */} 31 | 35 | 36 | {/* API */} 37 | 41 | 45 | 49 | 53 | 57 | 61 | 65 | 69 | 73 | 77 | 81 | 82 | {/* Support */} 83 | 87 | 88 | {/* Default */} 89 | 90 | 91 |
92 | 93 |
94 |
95 | ); 96 | } 97 | -------------------------------------------------------------------------------- /docs/src/components/app/header/app-header.scss: -------------------------------------------------------------------------------- 1 | @import "../../../assets/styles/rainbowify.scss"; 2 | 3 | $height: 120px; 4 | $width: 185px; 5 | 6 | $multiplier: 64 / 120; 7 | 8 | .app-header { 9 | @include rainbowify(background-color); 10 | align-items: flex-start; 11 | background-image: linear-gradient(90deg, #202020, transparent, #202020); 12 | display: flex; 13 | max-height: 80px; 14 | min-height: 80px; 15 | justify-content: center; 16 | width: 100%; 17 | 18 | > div { 19 | align-items: center; 20 | background-color: #202020; 21 | display: flex; 22 | height: calc(100% - 2px); 23 | justify-content: center; 24 | width: 100%; 25 | 26 | > h1 { 27 | margin: 0; 28 | 29 | > a { 30 | align-items: center; 31 | background-position: right center; 32 | background-repeat: no-repeat; 33 | background-size: auto 100%; 34 | color: #F0F0F0; 35 | display: inline-flex; 36 | font-family: "Roboto", sans-serif; 37 | justify-content: flex-end; 38 | font-size: ($height - 5px) * $multiplier; 39 | height: $height * $multiplier; 40 | text-decoration: none; 41 | 42 | > svg { 43 | margin: 0 -0.25em; 44 | z-index: 1; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/app/header/github-banner/app-header-github-banner.tsx: -------------------------------------------------------------------------------- 1 | import React, { useGlobal } from 'reactn'; 2 | import './app-header-github-banner.scss'; 3 | 4 | 5 | 6 | const arm: string = 7 | 'M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 ' + 8 | '15 9 16'; 9 | 10 | const BACKGROUND_COLOR: string = '#202020'; 11 | 12 | const body: string = 13 | 'M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 ' + 14 | '12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 ' + 15 | '6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z'; 16 | 17 | const SIZE: number = 69; // nice 18 | 19 | 20 | 21 | export default function AppHeaderGitHubBanner() { 22 | const [ fill ] = useGlobal('color'); 23 | return ( 24 | 31 | 38 | 43 | 44 | 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /docs/src/components/app/header/github-banner/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header-github-banner'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/header/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header'; 2 | -------------------------------------------------------------------------------- /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/app/header/react-logo/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-header-react-logo'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app'; 2 | -------------------------------------------------------------------------------- /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/app/navigation/app-navigation.tsx: -------------------------------------------------------------------------------- 1 | import React, { useGlobal } from 'reactn'; 2 | import './app-navigation.scss'; 3 | import ListItem from './list-item'; 4 | 5 | 6 | 7 | type Page = [ string, string ]; 8 | 9 | 10 | 11 | const PAGES: { [key: string]: Page[] } = { 12 | API: [ 13 | [ 'addCallback', 'addCallback' ], 14 | [ 'addReducer', 'addReducer' ], 15 | [ 'createProvider', 'createProvider' ], 16 | [ 'getGlobal', 'getGlobal' ], 17 | [ 'removeCallback', 'removeCallback' ], 18 | [ 'resetGlobal', 'resetGlobal' ], 19 | [ 'setGlobal', 'setGlobal' ], 20 | [ 'useGlobal', 'useGlobal' ], 21 | [ 'useGlobalReducer', 'useGlobalReducer' ], 22 | [ 'withGlobal', 'withGlobal' ], 23 | ], 24 | EXAMPLES: [ 25 | [ 'async-example', 'Asynchronous' ], 26 | [ 'counter-example', 'Counter' ], 27 | [ 'shopping-cart-example', 'Shopping Cart' ], 28 | [ 'todo-example', 'TODO' ], 29 | ], 30 | GETTING_STARTED: [ 31 | [ 'install', 'Installation' ], 32 | ], 33 | }; 34 | 35 | 36 | 37 | export default function AppNavigation() { 38 | const [ backgroundColor ] = useGlobal('color'); 39 | return ( 40 | 62 | ); 63 | } 64 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-navigation'; 2 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/list-item/app-navigation-list-item.scss: -------------------------------------------------------------------------------- 1 | @import "../../../../assets/styles/media.scss"; 2 | @import "../../../../assets/styles/rainbowify.scss"; 3 | 4 | .app-navigation-list-item { 5 | display: flex; 6 | flex-direction: column; 7 | 8 | &-selected { 9 | @include rainbowify(color); 10 | } 11 | 12 | > strong { 13 | 14 | > a { 15 | color: #F0F0F0; 16 | text-decoration: none; 17 | } 18 | } 19 | 20 | > ul { 21 | list-style-type: none; 22 | margin: 0; 23 | padding: 0 0 0.25em 0.5em; 24 | 25 | > li { 26 | 27 | &.app-navigation-list-item-selected { 28 | @include rainbowify(color); 29 | padding: 0.125em 0; 30 | } 31 | 32 | > a { 33 | color: #F0F0F0; 34 | display: inline-block; 35 | padding: 0.125em 0; 36 | text-decoration: none; 37 | } 38 | } 39 | } 40 | 41 | @include mobile { 42 | $sublinks-height: 2em; 43 | 44 | padding-bottom: $sublinks-height; 45 | 46 | &:not(:first-child) { 47 | padding-left: 0.5em; 48 | } 49 | 50 | &:not(:last-child) { 51 | padding-right: 0.5em; 52 | } 53 | 54 | &.app-navigation-list-item-selected { 55 | display: flex; 56 | flex-direction: column; 57 | 58 | > ul { 59 | bottom: 0; 60 | display: flex; 61 | flex-direction: row; 62 | left: 0; 63 | overflow: auto; 64 | position: absolute; 65 | right: 0; 66 | 67 | > li { 68 | max-height: $sublinks-height; 69 | min-height: $sublinks-height; 70 | line-height: $sublinks-height; 71 | padding-bottom: 0; 72 | white-space: nowrap; 73 | } 74 | } 75 | } 76 | 77 | &.app-navigation-list-item-unselected > ul { 78 | display: none; 79 | } 80 | 81 | > ul { 82 | padding: 0 0.5em; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /docs/src/components/app/navigation/list-item/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './app-navigation-list-item'; 2 | -------------------------------------------------------------------------------- /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/about/about.tsx: -------------------------------------------------------------------------------- 1 | import React, { useDispatch, withGlobal } from 'reactn'; 2 | import './about.scss'; 3 | 4 | interface Props { 5 | color: string; 6 | } 7 | 8 | export default withGlobal( 9 | ({ color }) => ({ color }), 10 | )( 11 | function About({ color }: Props): JSX.Element { 12 | const setColor = useDispatch('setColor'); 13 | setColor('#61DAFB'); 14 | return <> 15 |

16 | ReactN is a global state management solution for{' '} 17 | ReactJS. 18 |

19 |

20 | ReactN adheres to the following design philosophy, 21 |

22 |
26 | What if global state management were built into React itself? 27 |
28 | 38 | ; 39 | }, 40 | ); 41 | -------------------------------------------------------------------------------- /docs/src/components/routes/about/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './about'; 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-callback/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './add-callback'; 2 | -------------------------------------------------------------------------------- /docs/src/components/routes/add-reducer/add-reducer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'reactn'; 2 | 3 | export default function AddReducer(): JSX.Element { 4 | return ( 5 | <> 6 |

Use addReducer to add a reducer to your global state.

7 |

8 | The first parameter is the name of your reducer. You will access your 9 | reducer by this name: this.global.reducerName or 10 | useGlobalReducer('reducerName'). 11 |

12 |

13 | The second parameter is the reducer function. The reducer function that 14 | you write has two parameters: first, the global state; second, the 15 | value passed to the reducer. The reducer function that you use has one 16 | parameter: the value to pass to the reducer. 17 |

18 | EXAMPLE1 19 |

20 | For a class component, the analogous method is{' '} 21 | this.global.increment(value). 22 |

23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /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 |