├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── index.ts ├── package-lock.json ├── package.json ├── resources ├── convert-flying-ducks-to-this.png ├── example-of-flying-ducks.png ├── real-example-with-render.png ├── real-example.png └── usage-example.png ├── tsconfig.json ├── yarn-error.log └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | resources -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2019 Edvinas Pranka(@epranka, https:///www.kodmina.lt) 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-combine-providers 2 | ======================== 3 | 4 | tweet 6 | 7 | 8 | follow on Twitter 10 | 11 | 12 | 13 | 14 | In React.js, libraries such as ReactRouter, StyledComponents SSR, ReactHelmet and many other has providers. If you has a more than four providers in the root of the application, your entry file usually looks like as flying ducks in the sky 15 | 16 | ![example-of-flying-ducks](https://raw.githubusercontent.com/epranka/react-combine-providers/master/resources/example-of-flying-ducks.png) 17 | 18 | This module lets avoid it, and makes flying ducks more readable 19 | 20 | ![convert-flying-ducks-to-this](https://raw.githubusercontent.com/epranka/react-combine-providers/master/resources/convert-flying-ducks-to-this.png) 21 | 22 | ### Install 23 | 24 | ``` 25 | npm install --save react-combine-providers 26 | ``` 27 | 28 | or 29 | 30 | ``` 31 | yarn install react-combine-providers 32 | ``` 33 | 34 | ### Import module 35 | 36 | ```js 37 | import { combineProviders } from "react-combine-providers"; 38 | ``` 39 | 40 | ### Usage 41 | 42 | ![usage-example](https://raw.githubusercontent.com/epranka/react-combine-providers/master/resources/usage-example.png) 43 | 44 | ### Real example 45 | 46 | ![real-example](https://raw.githubusercontent.com/epranka/react-combine-providers/master/resources/real-example.png) 47 | 48 | ### Real example with render 49 | 50 | If you have problems with state loose, use render method instead 51 | 52 | ![real-example-with-render](https://raw.githubusercontent.com/epranka/react-combine-providers/master/resources/real-example-with-render.png) 53 | 54 | 55 | ### Author 56 | 57 | Edvinas pranka 58 | 59 | [@epranka](https://twitter.com/epranka) 60 | 61 | https://www.kodmina.lt 62 | 63 | # License 64 | 65 | ISC License 66 | 67 | Copyright (c) 2019 Edvinas Pranka(@epranka, https:///www.kodmina.lt) 68 | 69 | Permission to use, copy, modify, and/or distribute this software for any 70 | purpose with or without fee is hereby granted, provided that the above 71 | copyright notice and this permission notice appear in all copies. 72 | 73 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 74 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 75 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 76 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 77 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 78 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 79 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 80 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export declare class CombineProviders { 3 | private stack; 4 | constructor(); 5 | private getNode; 6 | push

(Component: React.ComponentType

, props?: P): void; 7 | private createProvidersTree; 8 | render(children: any): any; 9 | master(): ({ children }: { 10 | children: any; 11 | }) => any; 12 | } 13 | export declare const combineProviders: () => CombineProviders; 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | var __importStar = (this && this.__importStar) || function (mod) { 14 | if (mod && mod.__esModule) return mod; 15 | var result = {}; 16 | if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; 17 | result["default"] = mod; 18 | return result; 19 | }; 20 | Object.defineProperty(exports, "__esModule", { value: true }); 21 | var React = __importStar(require("react")); 22 | var CombineProviders = /** @class */ (function () { 23 | function CombineProviders() { 24 | this.stack = []; 25 | this.stack = []; 26 | } 27 | CombineProviders.prototype.getNode = function (Component) { 28 | // @ts-ignore 29 | return this.stack.find(function (node) { 30 | return node.Component === Component; 31 | }); 32 | }; 33 | CombineProviders.prototype.push = function (Component, props) { 34 | // @ts-ignore 35 | var node = this.getNode(Component); 36 | if (node) { 37 | node.props = props; 38 | } 39 | else { 40 | // @ts-ignore 41 | this.stack.push({ Component: Component, props: props }); 42 | } 43 | }; 44 | CombineProviders.prototype.createProvidersTree = function (stack, index, children) { 45 | if (index === void 0) { index = 0; } 46 | var isLastNode = index === stack.length - 1; 47 | var providerNode = stack[index]; 48 | var component = providerNode.Component; 49 | var props = providerNode.props; 50 | if (isLastNode) { 51 | return React.createElement(component, __assign({}, (props || {}), { children: children })); 52 | } 53 | else { 54 | return React.createElement(component, props, this.createProvidersTree(stack, ++index, children)); 55 | } 56 | }; 57 | CombineProviders.prototype.render = function (children) { 58 | return this.createProvidersTree(this.stack, 0, children); 59 | }; 60 | CombineProviders.prototype.master = function () { 61 | var _this = this; 62 | return function (_a) { 63 | var children = _a.children; 64 | return _this.createProvidersTree(_this.stack, 0, children); 65 | }; 66 | }; 67 | return CombineProviders; 68 | }()); 69 | exports.CombineProviders = CombineProviders; 70 | exports.combineProviders = function () { 71 | return new CombineProviders(); 72 | }; 73 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | interface ProviderNode { 3 | Component: React.ComponentType; 4 | props?: any; 5 | } 6 | export class CombineProviders { 7 | private stack: ProviderNode[] = []; 8 | constructor() { 9 | this.stack = []; 10 | } 11 | 12 | private getNode(Component: React.ComponentType) { 13 | // @ts-ignore 14 | return this.stack.find(node => { 15 | return node.Component === Component; 16 | }); 17 | } 18 | 19 | public push

(Component: React.ComponentType

, props?: P) { 20 | // @ts-ignore 21 | const node = this.getNode(Component); 22 | if (node) { 23 | node.props = props; 24 | } else { 25 | // @ts-ignore 26 | this.stack.push({ Component, props }); 27 | } 28 | } 29 | 30 | private createProvidersTree( 31 | stack: ProviderNode[], 32 | index: number = 0, 33 | children 34 | ) { 35 | const isLastNode = index === stack.length - 1; 36 | const providerNode = stack[index]; 37 | const component = providerNode.Component; 38 | const props = providerNode.props; 39 | if (isLastNode) { 40 | return React.createElement(component, { 41 | ...(props || {}), 42 | children: children 43 | }); 44 | } else { 45 | return React.createElement( 46 | component, 47 | props, 48 | this.createProvidersTree(stack, ++index, children) 49 | ); 50 | } 51 | } 52 | 53 | public render(children) { 54 | return this.createProvidersTree(this.stack, 0, children); 55 | } 56 | 57 | public master() { 58 | return ({ children }) => { 59 | return this.createProvidersTree(this.stack, 0, children); 60 | }; 61 | } 62 | } 63 | 64 | export const combineProviders = () => { 65 | return new CombineProviders(); 66 | }; 67 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-combine-providers", 3 | "version": "0.9.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/prop-types": { 8 | "version": "15.5.9", 9 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.9.tgz", 10 | "integrity": "sha512-Nha5b+jmBI271jdTMwrHiNXM+DvThjHOfyZtMX9kj/c/LUj2xiLHsG/1L3tJ8DjAoQN48cHwUwtqBotjyXaSdQ==", 11 | "dev": true 12 | }, 13 | "@types/react": { 14 | "version": "16.8.3", 15 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.3.tgz", 16 | "integrity": "sha512-PjPocAxL9SNLjYMP4dfOShW/rj9FDBJGu3JFRt0zEYf77xfihB6fq8zfDpMrV6s82KnAi7F1OEe5OsQX25Ybdw==", 17 | "dev": true, 18 | "requires": { 19 | "@types/prop-types": "15.5.9", 20 | "csstype": "2.6.2" 21 | } 22 | }, 23 | "csstype": { 24 | "version": "2.6.2", 25 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz", 26 | "integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow==", 27 | "dev": true 28 | }, 29 | "js-tokens": { 30 | "version": "4.0.0", 31 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 32 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 33 | }, 34 | "loose-envify": { 35 | "version": "1.4.0", 36 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 37 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 38 | "requires": { 39 | "js-tokens": "4.0.0" 40 | } 41 | }, 42 | "object-assign": { 43 | "version": "4.1.1", 44 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 45 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 46 | }, 47 | "prop-types": { 48 | "version": "15.7.2", 49 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 50 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 51 | "requires": { 52 | "loose-envify": "1.4.0", 53 | "object-assign": "4.1.1", 54 | "react-is": "16.8.2" 55 | } 56 | }, 57 | "react": { 58 | "version": "16.8.2", 59 | "resolved": "https://registry.npmjs.org/react/-/react-16.8.2.tgz", 60 | "integrity": "sha512-aB2ctx9uQ9vo09HVknqv3DGRpI7OIGJhCx3Bt0QqoRluEjHSaObJl+nG12GDdYH6sTgE7YiPJ6ZUyMx9kICdXw==", 61 | "requires": { 62 | "loose-envify": "1.4.0", 63 | "object-assign": "4.1.1", 64 | "prop-types": "15.7.2", 65 | "scheduler": "0.13.2" 66 | } 67 | }, 68 | "react-is": { 69 | "version": "16.8.2", 70 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz", 71 | "integrity": "sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==" 72 | }, 73 | "scheduler": { 74 | "version": "0.13.2", 75 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.2.tgz", 76 | "integrity": "sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==", 77 | "requires": { 78 | "loose-envify": "1.4.0", 79 | "object-assign": "4.1.1" 80 | } 81 | }, 82 | "typescript": { 83 | "version": "3.3.3", 84 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3.tgz", 85 | "integrity": "sha512-Y21Xqe54TBVp+VDSNbuDYdGw0BpoR/Q6wo/+35M8PAU0vipahnyduJWirxxdxjsAkS7hue53x2zp8gz7F05u0A==", 86 | "dev": true 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-combine-providers", 3 | "version": "0.9.6", 4 | "description": "Avoid flying ducks in react providers tree", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "build": "tsc --declaration" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/epranka/react-combine-providers.git" 14 | }, 15 | "keywords": [ 16 | "react", 17 | "reactjs", 18 | "combine", 19 | "providers", 20 | "javascript", 21 | "es6", 22 | "typescript" 23 | ], 24 | "author": "Edvinas Pranka (@epranka, https://www.kodmina.lt)", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/epranka/react-combine-providers/issues" 28 | }, 29 | "homepage": "https://github.com/epranka/react-combine-providers#readme", 30 | "peerDependencies": { 31 | "react": ">=16" 32 | }, 33 | "devDependencies": { 34 | "@types/react": "^16.8.3", 35 | "typescript": "3.2.0-dev.20181006" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /resources/convert-flying-ducks-to-this.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/react-combine-providers/c925232f2dc6ba7e42acbdb654d5540eb7df4ff0/resources/convert-flying-ducks-to-this.png -------------------------------------------------------------------------------- /resources/example-of-flying-ducks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/react-combine-providers/c925232f2dc6ba7e42acbdb654d5540eb7df4ff0/resources/example-of-flying-ducks.png -------------------------------------------------------------------------------- /resources/real-example-with-render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/react-combine-providers/c925232f2dc6ba7e42acbdb654d5540eb7df4ff0/resources/real-example-with-render.png -------------------------------------------------------------------------------- /resources/real-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/react-combine-providers/c925232f2dc6ba7e42acbdb654d5540eb7df4ff0/resources/real-example.png -------------------------------------------------------------------------------- /resources/usage-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epranka/react-combine-providers/c925232f2dc6ba7e42acbdb654d5540eb7df4ff0/resources/usage-example.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "skipLibCheck": true, 6 | "strict": false, 7 | "esModuleInterop": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /yarn-error.log: -------------------------------------------------------------------------------- 1 | Arguments: 2 | /home/epranka/.nvm/versions/node/v12.3.1/bin/node /usr/share/yarn/bin/yarn.js add 3.2.0-dev.20181006 -D 3 | 4 | PATH: 5 | /home/epranka/.nvm/versions/node/v12.3.1/bin:/home/epranka/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin 6 | 7 | Yarn version: 8 | 1.16.0 9 | 10 | Node version: 11 | 12.3.1 12 | 13 | Platform: 14 | linux x64 15 | 16 | Trace: 17 | Error: https://registry.yarnpkg.com/3.2.0-dev.20181006: Not found 18 | at Request.params.callback [as _callback] (/usr/share/yarn/lib/cli.js:66091:18) 19 | at Request.self.callback (/usr/share/yarn/lib/cli.js:129590:22) 20 | at Request.emit (events.js:200:13) 21 | at Request. (/usr/share/yarn/lib/cli.js:130562:10) 22 | at Request.emit (events.js:200:13) 23 | at IncomingMessage. (/usr/share/yarn/lib/cli.js:130484:12) 24 | at Object.onceWrapper (events.js:288:20) 25 | at IncomingMessage.emit (events.js:205:15) 26 | at endReadableNT (_stream_readable.js:1137:12) 27 | at processTicksAndRejections (internal/process/task_queues.js:84:9) 28 | 29 | npm manifest: 30 | { 31 | "name": "react-combine-providers", 32 | "version": "0.9.4", 33 | "description": "Avoid flying ducks in react providers tree", 34 | "main": "index.js", 35 | "types": "index.d.ts", 36 | "scripts": { 37 | "test": "echo \"Error: no test specified\" && exit 1", 38 | "build": "tsc --declaration" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "git+https://github.com/epranka/react-combine-providers.git" 43 | }, 44 | "keywords": [ 45 | "react", 46 | "reactjs", 47 | "combine", 48 | "providers", 49 | "javascript", 50 | "es6", 51 | "typescript" 52 | ], 53 | "author": "Edvinas Pranka (@epranka, https://www.kodmina.lt)", 54 | "license": "ISC", 55 | "bugs": { 56 | "url": "https://github.com/epranka/react-combine-providers/issues" 57 | }, 58 | "homepage": "https://github.com/epranka/react-combine-providers#readme", 59 | "peerDependencies": { 60 | "react": ">=16" 61 | }, 62 | "devDependencies": { 63 | "@types/react": "^16.8.3", 64 | "typescript": "^3.3.3" 65 | } 66 | } 67 | 68 | yarn manifest: 69 | No manifest 70 | 71 | Lockfile: 72 | No lockfile 73 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/prop-types@*": 6 | version "15.7.1" 7 | resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6" 8 | integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg== 9 | 10 | "@types/react@^16.8.3": 11 | version "16.8.19" 12 | resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.19.tgz#629154ef05e2e1985cdde94477deefd823ad9be3" 13 | integrity sha512-QzEzjrd1zFzY9cDlbIiFvdr+YUmefuuRYrPxmkwG0UQv5XF35gFIi7a95m1bNVcFU0VimxSZ5QVGSiBmlggQXQ== 14 | dependencies: 15 | "@types/prop-types" "*" 16 | csstype "^2.2.0" 17 | 18 | csstype@^2.2.0: 19 | version "2.6.5" 20 | resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.5.tgz#1cd1dff742ebf4d7c991470ae71e12bb6751e034" 21 | integrity sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA== 22 | 23 | typescript@3.2.0-dev.20181006: 24 | version "3.2.0-dev.20181006" 25 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.0-dev.20181006.tgz#87386f1f19250afaceb65eea26e5cfa223f9ad8e" 26 | integrity sha512-Hxbkj5GZAUyaRZKc4q2wBeCBerO5yymUehnPHFLM2nYWdTmnZGKKfnCbgrwRgIBSYmrhGQZChoc1ksOi3+9LcQ== 27 | --------------------------------------------------------------------------------