Modularize and abstract react component functionality - javascript

I have below a working component that allows for a checkbox all and checkboxes. It works perfectly. However I hate the idea that I'm stuck carrying all of this code around every time I want to use this feature. I'm looking for a way within react to make this modular? Is this
It doesn't modularize the entire functionality of the "input checked all" functionality in one place. I have to move the getInitialState variables and changeHandlers with each usage.
I think of it like if the "input checkbox all" functionally was native within HTML, how would we use it? We would need only supply the attributes to the elements and they would reference each other and all of the handlers would occur under the hood, it would be simple to use. My goal with this example is to have HTML level simplicity. The code I show above doesn't achieve that because it's tied down to function handlers and state initializers. Does react provide a way of abstracting this?
Below is my desired API for this component.
Here's the working example.
The main problems are:
The component functionality is indifferent to the parent, meaning the paren't doesn't need to store the information for the handlers and state.
The code currently manually tracks state for each checkbox, meaning there's no way to dynamically find how many of an a checkbox is in the DOM without stating it.
Overall modularity and ease-of use.
Here's the code:
var InputCheckbox = React.createClass({
getDefaultProps: function () {
return {
checked: false
}
},
render: function () {
return (
<input
checked={this.props.checked}
type='checkbox'
{...this.props}/>
)
}
})
var Test = React.createClass({
getInitialState: function () {
return {
checked: [false, false, false]
}
},
selectAll: function (event) {
// Set all checked states to true
this.setState({
checked: this.state.checked.map(function () {
return event.target.checked
})
})
},
handleChange: function (index, event) {
var checked = this.state.checked
checked[index] = event.target.checked
this.setState({
checked: checked
})
},
render: function () {
var isAllChecked = this.state.checked.filter(function (c) {
return c
}).length === this.state.checked.length
return (
<div>
Select All:
<InputCheckbox onChange={this.selectAll} checked={isAllChecked}/>
<br/>
<InputCheckbox checked={this.state.checked[0]} onChange={this.handleChange.bind(this, 0)}/>
<br/>
<InputCheckbox checked={this.state.checked[1]} onChange={this.handleChange.bind(this, 1)}/>
<br/>
<InputCheckbox checked={this.state.checked[2]} onChange={this.handleChange.bind(this, 2)}/>
<br/>
</div>
)
}
})
React.render(<Test/>, document.body)
Ideally I can just use it like this:
var Checkbox = require('./Checkbox')
var Test = React.createClass({
render: function () {
return (
<div>
<Checkbox id="alpha"/>
<Checkbox htmlFor="alpha"/>
<Checkbox htmlFor="alpha"/>
<Checkbox htmlFor="alpha"/>
</div>
)
}
})

The following example is in the right general direction I think, the general idea is to introduce a wrapper component for the related boxes, and then walk through the children in that component to tie them together.
var CheckAll = React.createClass({
render() {
return <input type="checkbox" {...this.props} />
}
});
var Checkbox = React.createClass({
render() {
return <input type="checkbox" {...this.props} />
}
});
var CheckboxGroup = React.createClass({
setAll(to) {
var result = {};
Object.keys(this.props.boxes).forEach(k => result[k] = to)
this.props.onChange(result);
},
setOne(name, to) {
var result = {};
Object.keys(this.props.boxes).forEach(k => result[k] = this.props.boxes[k])
result[name] = to;
this.props.onChange(result);
},
enrichChild(child) {
var boxes = this.props.boxes;
var all = Object.keys(boxes).every(k => boxes[k]);
if (child.type == CheckAll) {
return React.cloneElement(child, { checked: all,
onChange: () => this.setAll(!all)
});
} else if (child.type == Checkbox) {
var name = child.props.name;
return React.cloneElement(child, { checked: !!boxes[name],
onChange: ({target}) => this.setOne(name, target.checked)
});
} else {
return child;
}
},
render() {
return (
<div>
{React.Children.map(this.props.children, this.enrichChild)}
</div>
)
}
});
var Test = React.createClass({
getInitialState: function () {
return {
boxes: {
a: true,
b: false,
c: false,
}
}
},
render: function () {
return (
<div>
<CheckboxGroup
boxes={this.state.boxes}
onChange={boxes => this.setState({boxes})}
>
<CheckAll />
<Checkbox name="a" />
<Checkbox name="b" />
<Checkbox name="c" />
</CheckboxGroup>
</div>
)
}
})
React.render(<Test/>, document.body)
Here's a jsbin - https://jsbin.com/zomuxolevo/1/edit?js,output
To allow for more flexibility with the children, you'd need to recursively walk them using something like this gist https://gist.github.com/dandelany/1ff06f4fa1f8d6f89c5e
var RecursiveChildComponent = React.createClass({
render() {
return <div>
{this.recursiveCloneChildren(this.props.children)}
</div>
},
recursiveCloneChildren(children) {
return React.Children.map(children, child => {
if(!_.isObject(child)) return child;
var childProps = {someNew: "propToAdd"};
childProps.children = this.recursiveCloneChildren(child.props.children);
return React.cloneElement(child, childProps);
})
}
})

I hacked this together using some jQuery, and lodash.
Here's the example running.
Note, this example goes into the DOM to get the data needed. None of the state of these checkboxes are stored by the component. As far as I can tell there is no true "React" way to do this. (I am very open to suggestion.)
var Checkbox = React.createClass({
componentDidMount: function () {
var component = React.findDOMNode(this)
var $component = $(component)
if ($component.attr('id')) {
var selector = 'input'
selector += '[data-component=Checkbox]'
selector += '[for=' + $component.attr('id') + ']'
var $forComponents = $(selector)
$component.on('change', function () {
var value = $component.prop('checked')
$forComponents.each(function () {
$forComponent = $(this)
$forComponent.prop('checked', value)
})
})
$forComponents.on('change', function () {
var values = $forComponents.map(function () {
var $forComponent = $(this)
var value = $forComponent.prop('checked')
return value
})
var simple = _.chain(values).unique().value()
if (simple.length === 1 && simple[0] === true) {
$component.prop('checked', true)
} else {
$component.prop('checked', false)
}
})
}
},
render: function () {
return (
<input
type='checkbox'
data-component='Checkbox'
{...this.props}/>
)
}
})
var Test = React.createClass({
render: function () {
return (
<div>
Select All: <Checkbox id='alpha'/><br/>
<Checkbox htmlFor='alpha'/><br/>
<Checkbox htmlFor='alpha'/><br/>
<Checkbox htmlFor='alpha' defaultChecked/><br/>
<Checkbox htmlFor='alpha'/><br/>
<Checkbox htmlFor='alpha'/><br/>
</div>
)
}
})
React.render(<Test/>, document.body)

Related

How do I add a button component in ReactJS?

So I'm rendering components from an array 'values': ["hello", "world] successfully but I would like to add a button component so that every time it gets clicked, another empty field shows up. This is what it currently looks like:
but i would like it so that there is a button and every time I click on it, it renders another empty component to input text. Would it be correct to add a button component directly inside the my array_node.jsx file? Is what I'm doing correct so far? Would I also have to add some sort of newInput: function() in side the var AddButton = React.createClass({})? Thank you!
array_node.jsx:
{...
childChange: function(name, valid, value) {
// update state
this.state.values = this.props.values;
// Using regex to find last digits from 0-9
var pattern = /[0-9]/;
var match = name.match(pattern);
// Parse char into int
var i = parseInt(match);
this.state.values[i] = value;
this.setState(this.state);
// Call parent callback
this.props.callback(
this.props.name,
this.props.node.valid(this.state.values),
this.state.values
);
},
addItem: function(values){
},
render: function() {
var that = this;
return (
<div id = "form">
{this.props.values.map(function(v, i) {
return (
<div>
{(that.props.node.get().constructor.name === "Parent") ?
<ParentComponent
name={that.props.name + i}
key={i}
timer={that.props.timer}
callback={that.childChange}
values={v}
newParent={that.props.node.get()}
/>
:
<NodeComponent
name={that.props.name + i}
key={i}
timer={that.props.timer}
callback={that.childChange}
value={v}
newNode={that.props.node.get()}
/>
}
</div>
)
})}
</div>
)
}
});
return ArrayNodeComponent
var AddButton = React.createClass({
addItem: function() {
},
render: function() {
return(
<div id="create_new_entry">
</div>
)
}
})
formatoc:
var props = {
'name' : 'form',
'timer' : 1500,
'callback' : function(id, validity, value) {console.log(id, validity, value);},
'values': ["hello", "world"],
'node' : new FormatOC.ArrayNode({"__array__":"unique", "__type__":"string","__minimum__":1,"__maximum__":200,"__component__":"Input"},
)
}
React.render(React.createElement(ArrayNodeComponent, props), document.getElementById('react-component'));
You might add a button into your form within the render function.
Then listen to clicks and add a new empty element to your values list.
if you would like to propagate the changes to some parent component, you would have to pass the onClick handler from the parent component and update the values list there too.
import { Component } from 'react';
class ArrayNodeComponent extends Component {
// other code ...
// like your initialisation of your state
// and other functions
addEmptyItem() {
const { values } = this.state;
this.setState({
values: [...values, ""]
});
}
render() {
return (
<form id="test">
{
/* this is your values map routine, shortened */
this.props.values.map(function(v, i) { /*...*/ })
}
<button onClick={() => this.addEmptyItem()}>Add</button>
</form>
);
}
}
Btw in this simple scenario, it would not make sense to create a custom Button component.

How to get react to correctly render a list of removable inputs?

I'm trying to render a list of inputs in react and bind the input values to an array. I'm also trying to make it so the list items are removable. However, when I remove an item from the array, the input items are not updated how I would expect. Instead of removing the input that was removed from the middle of the array, the last input is removed and the middle input remains.
var Inputs = React.createClass({
getInitialState: function() {
return {
inputarr: ['']
};
},
render: function() {
var self = this;
return <div>{ this.state.inputarr.map(function (value, i) {
return <div key={i}><input onChange={function (e) {self.onChangeInput(i, e)}}/>
{ i < (self.state.inputarr.length - 1) && <button onClick={function () {self.onRemove(i)}}>x</button>}
</div>;
}) }</div>;
},
onChangeInput: function (i, e) {
this.state.inputarr[i] = e.target.value;
if (this.state.inputarr[this.state.inputarr.length - 1] !== '') {
this.state.inputarr.push('');
}
this.setState({
inputarr: this.state.inputarr.slice(0)
});
},
onRemove: function (i) {
this.state.inputarr.splice(i, 1);
this.setState({
inputarr: this.state.inputarr.slice(0)
});
}
});
ReactDOM.render(
<Inputs/>,
document.getElementById('container')
);
You can run this in this fiddle: https://jsfiddle.net/vvd7hex9/1/
What happens?
add something to the first input, a second will appear. Type in 3 different inputs.
remove the second input using the x button.
The last input is removed.
What I expected to happen
The middle input to be removed and only 2 inputs should contain the contents in the inputarr array.
Why does this happen? How can I fix it to remove the correct input?
Ahhhh, this is a classic javascript problem. It has to do with your map statement. You can read more about the specific details here, but what it boils down to is that when the click events actually fire, the value of i is equal to inputarr.length - 1. To fix this, you need some way of preserving the value of i during each loop. The easiest way to do this is to change the click event to this:
<button onClick={self.onRemove(i)}>x</button>
and change onRemove to this:
onRemove: function (i) {
var self = this;
return function(e) {
self.state.inputarr.splice(i, 1);
self.setState({
inputarr: this.state.inputarr.slice(0)
});
}
}
Some more info about closures can be found here if you're unfamiliar
I think it would be better to have separate Input component and App component.
Then you can create increment and decrement methods and pass them down from App to your Input components. I have build a little pen to show how you can achieve it.
I used some useful methods from lodash so take a look how them work.
https://codepen.io/dagman/pen/oYaYyL
The code itself.
class App extends React.Component {
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.state = {
quantity: [0],
};
}
increment(value) {
const { quantity } = this.state;
this.setState({
quantity: quantity.concat(_.last(quantity) + 1),
});
}
decrement(el) {
const { quantity } = this.state;
this.setState({ quantity: _.without(quantity, el) })
}
render() {
const inputs = this.state.quantity.map(x => (
<Input
increment={this.increment}
decrement={this.decrement}
key={x}
toDelete={x}
/>
));
return (
<form>
{inputs}
</form>
);
}
}
class Input extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onBtnClick = this.onBtnClick.bind(this);
this.state = {
value: '',
shouldIncrementQuantity: true,
};
}
onChange(e) {
const value = e.target.value;
this.setState({ value });
if(value.trim().length > 0 && this.state.shouldIncrementQuantity) {
this.setState({
shouldIncrementQuantity: false,
}, () => this.props.increment());
}
}
onBtnClick(e) {
e.preventDefault();
this.props.decrement(this.props.toDelete);
}
render() {
return (
<p className="input-field">
<input
type="text"
value={this.state.value}
onChange={this.onChange}
/>
<button onClick={this.onBtnClick}>x</button>
</p>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);

Get value from input and use on the button

I'am creating component with input element and button element.
I need to get the input value and use with button, for example. How can I do that?
Here's my code:
var InputSearch = React.createClass({
getInitialState: function() {
return {
value: 'pics'
}
},
handleChange: function() {
this.setState({
value: event.target.value
});
},
render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange} />
)
}
});
var ButtonSearch = React.createClass({
handleClick: function(event) {
console.log(this.state.value); // here's go the input value
},
render: function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
var Search = React.createClass({
render: function() {
return (
<div>
<InputSearch />
<ButtonSearch />
</div>
)
}
});
React.render(
<Search />,
document.getElementById('result')
);
One issue here is that you are breaking a good rule - separate smart and dumb components. https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0
The way to do this is to have a parent component that holds all the state and functionality of the children and passes all of this down as props...
//Our smart parent
var SearchContainer = React.createClass({
getInitialState : function() {
return {
value : 'pics'
}
},
handleInput : function(event) {
this.setState({value: event.target.value});
},
render : function() {
return (
<div>
<InputSearch value={this.state.value} onChange={this.handleInput} />
<ButtonSearch value={this.state.value} />
</div>
)
}
});
//Our dumb children
var InputSearch = React.createClass({
propTypes : {
onChange : React.PropTypes.func.isRequired,
value : React.PropTypes.string
},
render : function() {
return (
<input type="text" value={this.props.value} onChange={this.props.onChange} />
)
}
});
var ButtonSearch = React.createClass({
propTypes : {
value : React.PropTypes.string
},
handleClick : function() {
console.log(this.props.value); //log value
},
render : function() {
return (
<button onClick={this.handleClick}>GO! </button>
)
}
});
React.render(<Search />, document.getElementById('result'));
Here we pass the handler function down from parent to child so the input doesn't care what happens to the event it fires on change, it just needs to know that it has a prop called onChange that's a function and it invokes that.
The parent (SearchContainer) handles all of that functionality and passes the changed state down to both the button and the input...
hope that helps
Dan
You left out the event in your handleChange.
handleChange: function(event) {
this.setState({
value: event.target.value
});
},
The main architecture of react is the Parent Child / Master Slave principle.
If you want to pass values between components you have to create relations between.
Like for example
You create your master Component with few default states.
var MyMasterComponent = React.createClass({
getInitialState: function(){
...
},
render: function(){
return(
<ChilComponent1 textiwanttopass={this.state.text} />
);
}
});
With that method you are calling the render of another component within a master component. That way you can pass values from states into another component.
In that case you can access the passed text with this.props.textiwanttopass

React input checkbox select all component

I'm trying to build a proper react input checkbox select all component. The idea is that there is a component <InputCheckboxAll> and <InputCheckbox> and I'd be able to check the <InputCheckboxAll> and all of the <InputCheckbox> would be selected as well.
I'm having two issues.
If <InputCheckboxAll> is checked I can't unselect any of the <InputCheckbox>.
If all of the <InputCheckbox> are checked then <InputCheckboxAll> should be checked.
Here's the example.
var InputCheckboxAll = React.createClass({
handleChange: function (event) {
this.props.handleChange(event)
},
render: function () {
return (
<input
type='checkbox'
{...this.props}
onChange={this.handleChange} />
)
}
})
var InputCheckbox = React.createClass({
getInitialState: function () {
return {
checked: this.props.checked
}
},
render: function () {
var checkedValue = this.props.allChecked ? true : this.state.checked
return (
<input
checked={checkedValue}
type='checkbox'
{...this.props}/>
)
}
})
var Test = React.createClass({
getInitialState: function () { return {allChecked: false}; },
handleChange: function (event) {
var $elm = $(event.target)
var checked = $elm.prop('checked')
this.setState({
allChecked: checked
})
},
render: function () {
return (
<div>
Select All: <InputCheckboxAll handleChange={this.handleChange}/><br/>
<InputCheckbox allChecked={this.state.allChecked}/><br/>
<InputCheckbox allChecked={this.state.allChecked}/><br/>
<InputCheckbox allChecked={this.state.allChecked}/><br/>
</div>
)
}
})
React.render(<Test/>, document.body)
I think there could be some modifications to your implementation to achieve the desired results in a more React'esque form.
What you should get rid of first, is the InputCheckboxAll checkbox class, and the allChecked prop of the InputCheckbox class. A checkbox is a relatively dumb element, it should not know about concepts such as Everything is selected.
Instead, the checkbox should be implemented as an item that is simply either checked or unchecked.
var InputCheckbox = React.createClass({
getDefaultProps: function () {
return {
checked: false
}
},
render: function () {
return (
<input
checked={this.props.checked}
type='checkbox'
{...this.props}/>
)
}
})
The state of your app (concepts such as All Selected) should be managed from the main App, keeping lower level elements stateless. The state of the main app can simply represent the checked status of each of your checkboxes:
getInitialState: function () {
return {
// 3 checkboxes, all initialized as unchecked
checked: [false, false, false]
};
},
Now, you can recreate the render function to draw 3 checkboxes, plus your select all checkbox. Each <InputCheckbox> can be binded to its own data in the this.state.checked array. When the <Inputcheckbox> changes, we bind an index to the change handler, so we know which array element to modify.
render: function () {
// Recalculate if everything is checked each render, instead of storing it
var isAllChecked = this.state.checked.filter(function(c) {
return c;
}).length === this.state.checked.length;
return (
<div>
Select All: <InputCheckbox
onChange={this.selectAll}
checked={isAllChecked}/><br/>
<InputCheckbox
checked={this.state.checked[0]}
onChange={this.handleChange.bind(this, 0)}/><br/>
<InputCheckbox
checked={this.state.checked[1]}
onChange={this.handleChange.bind(this, 1)}/><br/>
<InputCheckbox
checked={this.state.checked[2]}
onChange={this.handleChange.bind(this, 2)}/><br/>
</div>
)
}
You don't need to store any state related to All Selected. Instead, it would be better to recalculate if everything is selected or not during every render. When the Select All checkbox is checked, we simply set every element of this.state.checked to true.
This also has the advantage of when you manually select all the checkboxes, the select all checkbox will check itself.
Here's a sample implementation:
https://jsfiddle.net/rsupvxry/
I came across to this thread very late. Below solution works for me with below structure.
There are 3 components which makes this feature clean and reusable.
CheckBox - Reusable Component
CheckBoxList - Reusable Component
CityList - End level where you can create states list or any list by cloning this.
Stack Blitz with Module - https://stackblitz.com/edit/react-check-box-list
Standlone Code is below
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>React: Select All or DeSelect All</title>
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script type="text/babel">
function CheckBox({name, value, tick, onCheck}) {
return (
<label>
<input
name={name}
type="checkbox"
value={value}
checked={tick || false}
onChange={onCheck}
/>
{value}
</label>
);
}
function CheckBoxList ({options, isCheckedAll, onCheck}) {
const checkBoxOptions = (
<div className="checkbox-list">
{options.map((option, index) => {
return (
<CheckBox key={index} name={option.name} value={option.value} tick={option.checked} onCheck={(e) => onCheck(option.value, e.target.checked)} />
);
})}
</div>
);
return (
<div className="checkbox-list">
<CheckBox name="select-all" value="ALL" tick={isCheckedAll} onCheck={(e) => onCheck('all', e.target.checked)} />
{checkBoxOptions}
</div>
);
}
class CityList extends React.Component {
constructor(props) {
super(props);
this.state = {
isAllSelected: false,
checkList: [
{
name: "city",
value: "bangalore",
checked: false,
},
{
name: "city",
value: "chennai",
checked: false,
},
{
name: "city",
value: "delhi",
checked: false,
}
]
};
}
onCheckBoxChange(checkName, isChecked) {
let isAllChecked = (checkName === 'all' && isChecked);
let isAllUnChecked = (checkName === 'all' && !isChecked);
const checked = isChecked;
const checkList = this.state.checkList.map((city, index) => {
if(isAllChecked || city.value === checkName) {
return Object.assign({}, city, {
checked,
});
} else if (isAllUnChecked) {
return Object.assign({}, city, {
checked: false,
});
}
return city;
});
let isAllSelected = (checkList.findIndex((item) => item.checked === false) === -1) || isAllChecked;
this.setState({
checkList,
isAllSelected,
});
}
render() {
return (<CheckBoxList options={this.state.checkList} isCheckedAll={this.state.isAllSelected} onCheck={this.onCheckBoxChange.bind(this)} />);
}
}
ReactDOM.render(
<CityList />,
document.getElementById('root')
);
</script>
</head>
<body>
<div id="root"></div>
</body>
</html>
There is a simple package that solves this problem grouped-checkboxes.
In your case the render function will look like this:
render: function () {
return (
<CheckboxGroup>
Select All: <AllCheckerCheckbox /><br/>
<Checkbox id="option-0" onChange={this.handleChange.bind(this, 0)}/><br/>
<Checkbox id="option-1" onChange={this.handleChange.bind(this, 1)}/><br/>
<Checkbox id="option-2" onChange={this.handleChange.bind(this, 2)}/><br/>
</CheckboxGroup>
)
}
More examples see Codesandbox example
I made the check all option how below:
JS code:
const checkAll = (e) => {
var value = false
if(e.target.checked){
value = true;
}
Array.from(document.querySelectorAll("input[name="+name+"]"))
.forEach((checkbox) => {
document.getElementById(checkbox.id).checked = value;
});
}
HTML code (Check All):
<input type="checkbox" name="all" onChange={checkAll} />
HTML code (Options):
<input type="checkbox" name={name} id={name+index} value={obj.id} />
The options was created using the loop where the id is unique for such checkbox.

React.js two-way binding on block elements

I'm trying to bind a value to a div with react so that I can maintain state for that element (eg. on-off) It looks like I should be using LinkedStateMixin, but my experiment below proves that react doesn't support arbitrary attributes for block level elements. Both elements have default values but the div e.target.value returns undefined from its onclick handler whereas the input element value has been properly set. Any idea how to bind data to the div? Thanks!
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onClick={handleClick} defaultValue={valueLink.value} />
<div onClick={handleClick} defaultValue={valueLink.value}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
http://jsfiddle.net/su8r5Lob/
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {
message: 'Hello!',
active: false
};
},
inputClick : function(e) {
console.log(e.target.value);
},
toggleActive : function(e){
console.log('div state', this.state.active);
var newState = !this.state.active;
this.setState({active: newState});
},
render: function () {
var cx = React.addons.classSet;
var valueLink = this.linkState('message');
var classes = cx({
'base-class': true,
'element-active': this.state.active
});
return (
<div>
<input type="text" onClick={this.inputClick} defaultValue={valueLink.value} />
<div onClick={this.toggleActive} className={classes}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
http://jsfiddle.net/su8r5Lob/1/
The reason your code does not work is because <div> elements do not have a value property. Only elements that receive user input have it. So when handleClick is called, valueLink.requestChange receives undefined as a parameter.
I've updated your Fiddle a little bit, and now it does support two-way binding for the onChange event.
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onChange={handleClick} value={valueLink.value} />
<input type="text" onChange={handleClick} value={valueLink.value} />
</div>
);
}
});
React.render(<Component />, document.body);
But, if you want to bind it to a div element, I give you this suggestion. I'm not sure if it is exactly what you expect, but here it is:
var Component = React.createClass({
mixins: [React.addons.LinkedStateMixin],
getInitialState: function() {
return {message: 'Hello!'};
},
render: function () {
var valueLink = this.linkState('message');
var handleClick = function(e) {
console.log(e.target.value);
valueLink.requestChange(e.target.value);
};
return (
<div>
<input type="text" onChange={handleClick} value={valueLink.value} />
<div onClick={handleClick.bind(this, {target: {value: 'someDivValue'}})} defaultValue={valueLink.value}>
{this.state.message}
</div>
</div>
);
}
});
React.render(<Component />, document.body);
Note that I gave the div a default value that is going to be set to the valueLink everytime the user clicks it. And I had to change the event on the input to onchange so it can update its value when the user types something.

Categories