├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── index.js ├── package.json ├── readme.md ├── tests ├── es5-deep-array.fixture.js ├── es5-deep.fixture.js ├── es5.fixture.js ├── getIn.spec.js ├── setup.js ├── simulate.spec.js └── updateIn.spec.js └── utils ├── getIn.js └── updateIn.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*.js] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 28 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tests 2 | .travis.yml 3 | .gitignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.0.0" -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var getIn = require('./utils/getIn'); 2 | var updateIn = require('./utils/updateIn'); 3 | 4 | /** 5 | * Extracted the linkedState implementation to its own function (instead of a mixin) 6 | * 7 | * @params {ReactElement} ctx The component's `this` 8 | * @params {str} path State key to be updated 9 | * @return {object} 10 | */ 11 | module.exports = function linkState(ctx, path) { 12 | return { 13 | value: getIn(ctx.state, path), 14 | 15 | requestChange: function setPartialState(value) { 16 | ctx.setState(updateIn( 17 | ctx.state, 18 | path, 19 | value 20 | )); 21 | } 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-link-state", 3 | "version": "0.1.2", 4 | "description": "A helper util to use LinkedStateMixin for ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha tests/*.spec.js --require=tests/setup.js" 8 | }, 9 | "author": "Kier Borromeo (http://srph.github.io)", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "chai": "^3.3.0", 13 | "jsdom": "^6.5.1", 14 | "mocha": "^2.3.3", 15 | "react": "^0.14.0", 16 | "react-addons-test-utils": "^0.14.0", 17 | "react-dom": "^0.14.0" 18 | }, 19 | "repository": "srph/react-link-state" 20 | } 21 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## react-link-state [![npm version](http://img.shields.io/npm/v/react-link-state.svg?style=flat-square)](https://npmjs.org/package/react-link-state?style=flat-square) [![Build Status](https://img.shields.io/travis/srph/react-link-state.svg?style=flat-square)](https://travis-ci.org/srph/react-link-state?branch=master) 2 | A helper function to use LinkedStateMixin for ES6 React Components. 3 | 4 | ## Removal of `valueLink` 5 | `valueLink` has been removed from React starting on `v16`. Still, this projects works just fine for older versions. 6 | 7 | If you'd like to future-proof your application, check out [@developit](https://github.com/developit)'s [`linkstate`](https://github.com/developit/linkstate). 8 | 9 | ## Installing 10 | ```bash 11 | npm install react-link-state --save 12 | ``` 13 | 14 | ## Usage 15 | ```es6 16 | import React from 'react'; 17 | import linkState from 'react-link-state'; 18 | 19 | export default MyForm extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | 23 | this.state = { 24 | username: '', 25 | password: '', 26 | toggle: false 27 | }; 28 | } 29 | 30 | render() { 31 | console.log(this.state); 32 | 33 | return ( 34 |
35 | 36 | 37 | 38 |
39 | ); 40 | } 41 | } 42 | ``` 43 | 44 | **Note**: Use [`checkedLink`](https://facebook.github.io/react/docs/two-way-binding-helpers.html) for checkboxes. 45 | 46 | Deep link-state is also supported! 47 | 48 | ```es6 49 | // Given that we have this state: 50 | // { 51 | // users: [{ 52 | // profile: { 53 | // first_name: '' 54 | // } 55 | // }] 56 | // } 57 | 58 | ``` 59 | 60 | ## Testing 61 | ``` 62 | npm test 63 | ``` 64 | 65 | You need node `v4.0.0` to runs the tests. See [issue](http://facebook.github.io/react/docs/test-utils.html#renderintodocument). 66 | 67 | > You will need to have window, window.document and window.document.createElement globally available before you import React. Otherwise React will think it can't access the DOM and methods like setState won't work. 68 | -------------------------------------------------------------------------------- /tests/es5-deep-array.fixture.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var input = React.DOM.input; 3 | var linkState = require('../'); 4 | 5 | var MyForm = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | users: [{ 9 | username: '', 10 | password: '', 11 | 12 | profile: { 13 | first_name: '', 14 | last_name: '' 15 | } 16 | }] 17 | }; 18 | }, 19 | 20 | render: function() { 21 | return input({ 22 | type: 'text', 23 | valueLink: linkState(this, 'users.0.profile.first_name') 24 | }); 25 | } 26 | }); 27 | 28 | module.exports = React.createFactory(MyForm); 29 | -------------------------------------------------------------------------------- /tests/es5-deep.fixture.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var input = React.DOM.input; 3 | var linkState = require('../'); 4 | 5 | var MyForm = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | user: { 9 | username: '', 10 | password: '', 11 | 12 | profile: { 13 | first_name: '', 14 | last_name: '' 15 | } 16 | } 17 | }; 18 | }, 19 | 20 | render: function() { 21 | return input({ 22 | type: 'text', 23 | valueLink: linkState(this, 'user.profile.first_name') 24 | }); 25 | } 26 | }); 27 | 28 | module.exports = React.createFactory(MyForm); 29 | -------------------------------------------------------------------------------- /tests/es5.fixture.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var input = React.DOM.input; 3 | var linkState = require('../'); 4 | 5 | var MyForm = React.createClass({ 6 | getInitialState: function() { 7 | return { 8 | username: '', 9 | password: '' 10 | }; 11 | }, 12 | 13 | render: function() { 14 | return input({ 15 | type: 'text', 16 | valueLink: linkState(this, 'username') 17 | }); 18 | } 19 | }); 20 | 21 | module.exports = React.createFactory(MyForm); 22 | -------------------------------------------------------------------------------- /tests/getIn.spec.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var getIn = require('../utils/getIn'); 3 | 4 | describe('getIn', function() { 5 | it('should work', function() { 6 | var obj = { yolo: { please: { swag: true } } }; 7 | var value = getIn(obj, 'yolo.please.swag'); 8 | 9 | expect(value).to.be.true; 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /tests/setup.js: -------------------------------------------------------------------------------- 1 | var jsdom = require('jsdom').jsdom; 2 | 3 | global.document = jsdom(''); 4 | global.window = document.defaultView; 5 | global.navigator = global.window.navigator; -------------------------------------------------------------------------------- /tests/simulate.spec.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var findDOMNode = require('react-dom').findDOMNode; 3 | var TestUtils = require('react-addons-test-utils'); 4 | var ES5Fixture = require('./es5.fixture'); 5 | var ES5DeepFixture = require('./es5-deep.fixture'); 6 | var ES5DeepArrayFixture = require('./es5-deep-array.fixture'); 7 | var expect = require('chai').expect; 8 | 9 | describe('linkState', function() { 10 | it('should change value of the state', function() { 11 | var Component = TestUtils.renderIntoDocument(ES5Fixture()); 12 | var node = findDOMNode(Component); 13 | 14 | node.value = 'yolo'; 15 | TestUtils.Simulate.change(node); 16 | expect(Component.state.username).to.equal('yolo'); 17 | }); 18 | 19 | describe('deep', function() { 20 | it('should work for objects', function() { 21 | var Component = TestUtils.renderIntoDocument(ES5DeepFixture()); 22 | var node = findDOMNode(Component); 23 | 24 | node.value = 'yolo'; 25 | TestUtils.Simulate.change(node); 26 | expect(Component.state.user.profile.first_name).to.equal('yolo'); 27 | }); 28 | 29 | it('should work for arrays', function() { 30 | var Component = TestUtils.renderIntoDocument(ES5DeepArrayFixture()); 31 | var node = findDOMNode(Component); 32 | 33 | node.value = 'yolo'; 34 | TestUtils.Simulate.change(node); 35 | expect(Component.state.users[0].profile.first_name).to.equal('yolo'); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /tests/updateIn.spec.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var updateIn = require('../utils/updateIn'); 3 | 4 | describe('updateIn', function() { 5 | it('should work', function() { 6 | var obj = { yolo: { please: { swag: true } } }; 7 | var updated = updateIn(obj, 'yolo.please.swag', false); 8 | 9 | expect(obj.yolo.please.swag).to.be.false; 10 | expect(obj).to.eql(updated); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /utils/getIn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Originally from: 3 | * https://github.com/tungd/react-catalyst/blob/master/src/catalyst/LinkedStateMixin.js 4 | * 5 | * @param {object} 6 | * @param {string} 7 | * @return {object} 8 | */ 9 | module.exports = function getIn(obj, path) { 10 | var stack = path.split('.'); 11 | 12 | while ( stack.length ) { 13 | obj = obj[stack.shift()]; 14 | } 15 | 16 | return obj; 17 | } 18 | -------------------------------------------------------------------------------- /utils/updateIn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Originally from: 3 | * https://github.com/tungd/react-catalyst/blob/master/src/catalyst/LinkedStateMixin.js 4 | * 5 | * @param {object} 6 | * @param {string} 7 | * @param {any} 8 | * @return {object} 9 | */ 10 | module.exports = function updateIn(obj, path, value) { 11 | var current = obj; 12 | var stack = path.split('.'); 13 | 14 | while ( stack.length > 1 ) { 15 | current = current[stack.shift()]; 16 | } 17 | current[stack.shift()] = value; 18 | 19 | return obj; 20 | } 21 | --------------------------------------------------------------------------------