├── .babelrc
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── dist
├── consumer.js
├── index.js
└── provider.js
├── package.json
└── src
├── __tests__
├── consumer-test.js
└── provider-test.js
├── consumer.js
├── index.js
└── provider.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "rules": {
4 | "strict": 0
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "5"
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Michele Bertoli
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/MicheleBertoli/react-data-provider)
2 |
3 | React Data Provider
4 | ===================
5 |
6 | The [Context](https://facebook.github.io/react/docs/context.html) is a great feature but dealing with it inside your components makes the code less readable and Facebook itself discourages the use of it.
7 | On the other hand, there are some scenarios where you actually need to share data from the root of your app to the rest of the tree.
8 |
9 | That's exactly what this package does for you:
10 | you pass some props to the `Provider`,
11 | you wrap your components into the `Consumer` and they receive all the needed props, transparently, from the Context
12 | (if those props exist, are valid and there are no local props with the same name).
13 |
14 | The benefits of this approach are:
15 | - no need to use the Context
16 | - components are easier to test
17 | - consistency in the way components receive data
18 |
19 | Installation
20 | ------------
21 |
22 | ```sh
23 | $ npm install react-data-provider --save
24 | ```
25 |
26 | Usage
27 | -----
28 |
29 | ```jsx
30 | import React from 'react'
31 | import {render} from 'react-dom'
32 | import {Provider, Consumer} from 'react-data-provider'
33 |
34 | const Button = React.createClass({
35 | propTypes: {
36 | text: React.PropTypes.string
37 | },
38 | render() {
39 | return
40 | },
41 | })
42 |
43 | const ConsumerButton = Consumer(Button)
44 |
45 | render(
46 |
47 |
48 | ,
49 | document.getElementById('root')
50 | )
51 | ```
52 |
53 | Test
54 | ----
55 |
56 | ```sh
57 | $ npm test
58 | ```
59 |
--------------------------------------------------------------------------------
/dist/consumer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
8 |
9 | var _react = require('react');
10 |
11 | var _react2 = _interopRequireDefault(_react);
12 |
13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14 |
15 | exports.default = function (Component) {
16 |
17 | return _react2.default.createClass({
18 |
19 | contextTypes: {
20 | __data: _react2.default.PropTypes.object
21 | },
22 |
23 | propExists: function propExists(prop) {
24 | return this.props[prop];
25 | },
26 | contextIsValid: function contextIsValid(prop) {
27 | return !Component.propTypes[prop](this.context.__data, prop);
28 | },
29 | contextToProps: function contextToProps() {
30 | var newProps = {};
31 | for (var prop in Component.propTypes) {
32 | if (!this.propExists(prop) && this.contextIsValid(prop)) {
33 | newProps[prop] = this.context.__data[prop];
34 | }
35 | }
36 | return newProps;
37 | },
38 | render: function render() {
39 | return _react2.default.createElement(Component, _extends({}, this.contextToProps(), this.props));
40 | }
41 | });
42 | };
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Consumer = exports.Provider = undefined;
7 |
8 | var _provider = require('./provider.js');
9 |
10 | var _provider2 = _interopRequireDefault(_provider);
11 |
12 | var _consumer = require('./consumer.js');
13 |
14 | var _consumer2 = _interopRequireDefault(_consumer);
15 |
16 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17 |
18 | exports.Provider = _provider2.default;
19 | exports.Consumer = _consumer2.default;
--------------------------------------------------------------------------------
/dist/provider.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _react = require('react');
8 |
9 | var _react2 = _interopRequireDefault(_react);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | exports.default = _react2.default.createClass({
14 | displayName: 'provider',
15 |
16 |
17 | childContextTypes: {
18 | __data: _react2.default.PropTypes.object
19 | },
20 |
21 | getChildContext: function getChildContext() {
22 | return {
23 | __data: this.props
24 | };
25 | },
26 |
27 | render: function render() {
28 | return this.props.children;
29 | }
30 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-data-provider",
3 | "version": "1.0.1",
4 | "description": "Because the Context is too mainstream",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "test": "jest",
8 | "build": "babel src -d dist --ignore __tests__"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/MicheleBertoli/react-data-provider.git"
13 | },
14 | "keywords": [
15 | "react",
16 | "react-component",
17 | "context"
18 | ],
19 | "author": "Michele Bertoli",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/MicheleBertoli/react-data-provider/issues"
23 | },
24 | "homepage": "https://github.com/MicheleBertoli/react-data-provider#readme",
25 | "devDependencies": {
26 | "babel-cli": "^6.5.1",
27 | "babel-eslint": "^5.0.0",
28 | "babel-jest": "^6.0.1",
29 | "babel-preset-es2015": "^6.5.0",
30 | "babel-preset-react": "^6.5.0",
31 | "jest-cli": "^0.8.2",
32 | "react": "^0.14.7",
33 | "react-addons-test-utils": "^0.14.7",
34 | "react-dom": "^0.14.7"
35 | },
36 | "jest": {
37 | "scriptPreprocessor": "/node_modules/babel-jest",
38 | "unmockedModulePathPatterns": [
39 | "/node_modules/react",
40 | "/node_modules/react-dom",
41 | "/node_modules/react-addons-test-utils",
42 | "/node_modules/fbjs"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/__tests__/consumer-test.js:
--------------------------------------------------------------------------------
1 | jest.dontMock('../provider')
2 | jest.dontMock('../consumer')
3 |
4 | import React from 'react'
5 | import TestUtils from 'react-addons-test-utils'
6 |
7 | const Provider = require('../provider').default
8 | const Consumer = require('../consumer').default
9 |
10 | describe('Consumer', () => {
11 |
12 | const Child = React.createClass({
13 | propTypes: {
14 | foo: React.PropTypes.string,
15 | },
16 | render() {
17 | return null
18 | },
19 | })
20 | const ConsumerChild = Consumer(Child)
21 |
22 | it('transforms the context to props for the specified types', () => {
23 |
24 | const tree = TestUtils.renderIntoDocument(
25 |
26 |
27 |
28 | )
29 | const child = TestUtils.findRenderedComponentWithType(tree, Child)
30 |
31 | expect(child.props.foo).toBe('bar')
32 |
33 | })
34 |
35 | it('transforms the context to props for the specified types only', () => {
36 |
37 | const tree = TestUtils.renderIntoDocument(
38 |
39 |
40 |
41 | )
42 | const child = TestUtils.findRenderedComponentWithType(tree, Child)
43 |
44 | expect(child.props.yo).toBeUndefined()
45 |
46 | })
47 |
48 | it('transforms the context to props only if they match the type', () => {
49 |
50 | const tree = TestUtils.renderIntoDocument(
51 |
52 |
53 |
54 | )
55 | const child = TestUtils.findRenderedComponentWithType(tree, Child)
56 |
57 | expect(child.props.foo).toBeUndefined()
58 |
59 | })
60 |
61 | it('transforms the context to props only if there are no local props with the same name', () => {
62 |
63 | const tree = TestUtils.renderIntoDocument(
64 |
65 |
66 |
67 | )
68 | const child = TestUtils.findRenderedComponentWithType(tree, Child)
69 |
70 | expect(child.props.foo).toBe('yo')
71 |
72 | })
73 |
74 | })
75 |
--------------------------------------------------------------------------------
/src/__tests__/provider-test.js:
--------------------------------------------------------------------------------
1 | jest.dontMock('../provider')
2 |
3 | import React from 'react'
4 | import TestUtils from 'react-addons-test-utils'
5 |
6 | const Provider = require('../provider').default
7 |
8 | describe('Provider', () => {
9 |
10 | const Child = React.createClass({
11 | contextTypes: {
12 | __data: React.PropTypes.object,
13 | },
14 | render() {
15 | return null
16 | },
17 | })
18 |
19 | it('creates a data context using the received props', () => {
20 |
21 | const tree = TestUtils.renderIntoDocument(
22 |
23 |
24 |
25 | )
26 | const child = TestUtils.findRenderedComponentWithType(tree, Child)
27 |
28 | expect(child.context.__data.foo).toBe('bar')
29 |
30 | })
31 |
32 | })
33 |
--------------------------------------------------------------------------------
/src/consumer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default Component => {
4 |
5 | return React.createClass({
6 |
7 | contextTypes: {
8 | __data: React.PropTypes.object,
9 | },
10 |
11 | propExists(prop) {
12 | return this.props[prop]
13 | },
14 |
15 | contextIsValid(prop) {
16 | return !Component.propTypes[prop](this.context.__data, prop)
17 | },
18 |
19 | contextToProps() {
20 | const newProps = {}
21 | for (let prop in Component.propTypes) {
22 | if (!this.propExists(prop) && this.contextIsValid(prop)) {
23 | newProps[prop] = this.context.__data[prop]
24 | }
25 | }
26 | return newProps
27 | },
28 |
29 | render() {
30 | return
31 | },
32 |
33 | })
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import Provider from './provider.js'
2 | import Consumer from './consumer.js'
3 |
4 | export {
5 | Provider,
6 | Consumer,
7 | }
8 |
--------------------------------------------------------------------------------
/src/provider.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default React.createClass({
4 |
5 | childContextTypes: {
6 | __data: React.PropTypes.object,
7 | },
8 |
9 | getChildContext: function() {
10 | return {
11 | __data: this.props,
12 | }
13 | },
14 |
15 | render() {
16 | return this.props.children
17 | },
18 |
19 | })
20 |
--------------------------------------------------------------------------------