Checkbox keeps state across components in React JS - javascript

I'm new in React JS, but I read about <input> that you have to save actual state in onChange like described here: React DOC - Forms
I have a list with a checkbox and I applied same behavior here in CampaignsRow
var campaignsData = [{Name: "First"}, {Name: "Second"}, {Name: "Third"}];
var CampaignsRow = React.createClass({
getInitialState: function() {
return {checked: false};
},
checkedChange: function (e) {
this.setState({checked: e.target.checked});
},
render: function() {
console.log(this.props.data.Name, this.state.checked);
return (
<div className="row">
<div className="cell checkbox">
<input type="checkbox" checked={this.state.checked} onChange={this.checkedChange}/>
</div>
<div className="cell campaignName">{this.props.data.Name}</div>
</div>
);
}
});
var CampaignsTable = React.createClass({
render: function() {
var rows = this.props.campaigns.map(function(campaign) {
return (
<CampaignsRow data={campaign}/>
);
});
return <div className="table">
<div className="row header">
<div className="cell checkbox"><input type="checkbox"/></div>
<div className="cell campaignName">Name</div>
</div>
{rows}
</div>
}
});
ReactDOM.render(<CampaignsTable campaigns={campaignsData} /> ,document.getElementById('reactContainer'));
My problem is, if I check the checkbox at the campaign with name First and then I remove first item by campaignsData.shift() (to simulate downloading new data from Server) and then render again, checkbox at Second campaign is checked.
What is the purpose of this.state when it is not attached to the instance. Render works fine, because in the console is printed Second true, so this.state.checked was moved from First to Second campaign.

You should add unique key property to multiple components, so that React can keep track of identities:
var rows = this.props.campaigns.map(function(campaign) {
return (
<CampaignsRow key={campaign.name} data={campaign}/>
);
});

Related

How to use the condition map function in ReactJS?

I am trying to display bootstrap carousel via ajax call using react js. Ajax receives json data consisting image name, content title and some meta information of per slide what I want to inject in DOM. So, I use the map function to generate all slides. My problem is, for the first slide I want to add a class active. But I do not know how to use condition in map().
In React, I have written: (in SliderWidget class, I have written a comment actually where I should use active class conditionally)
var HomeCarousel = React.createClass({
getInitialState: function() {
return {
data: []
}
},
componentDidMount: function() {
$.get("/api/slider", function(result) {
this.setState({
data: result
});
}.bind(this));
},
render: function() {
return (
<div id={"myCarousel"} className={"carousel slide"} data-ride="carousel">
{this.state.data.map((slider, i) => <SliderWidget key = {i} data = {slider} />)}
</div>
);
}
});
class SliderWidget extends React.Component {
render() {
return (
<div className={"item active"}> // here I want to use active class for the first slide
<img className={"first-slide"} src="images/pexels.jpeg" alt="First slide" />
<div className={"container"}>
<div className={"carousel-caption"}>
<h3>Research Indicates Breakfast is the Most Important Meal</h3>
<p><a className={"btn btn-primary"} href="#" role="button">Find Out More</a></p>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<HomeCarousel />, document.getElementById('react-home-carousel')
);
The i in the map callback is the loop index, so pass a property accordingly:
this.state.data.map( (slider, i) =>
<SliderWidget key={i} data={slider} active={i===0} />
)
Then in SliderWidget:
render() {
return (
<div className={"item" + this.props.active ? ' active' : ''}>...
)
}
Using classnames will make your life even easier.

Render JSX element based on condition

So I have a simple component within a web app I have been working on and I was wondering if there is a way I could render an element within this component based on the value of this.props.item.
here is my JSX:
var React = require("react"); var actions = require("../actions/SchoolActions");
module.exports = React.createClass({
deleteSchool: function(e){
e.preventDefault();
actions.deleteSchool(this.props.info);
},
render:function(){
return(
<div className="panel panel-default">
<div className="panel-heading">
{this.props.info.name}
<span className="pull-right text-uppercase delete-button" onClick={this.deleteSchool}>×</span>
</div>
<div className="panel-body">{this.props.info.tagline}</div>
</div>
)
} })
I wanted to be able to do something like this:
render:function(){
return(
code blah blah...
if (this.props.info = "nothing"){
<div className="panel-body">{this.props.info.tagline}</div>
}
...code blah blah
But I cannot write javascript withing the render function itself. Does anyone know how I could do this? Any help or advice is appreciated, thank you in advance.
You can use conditionally render using if and return the appropriate jsx
render(){
if(something){
return(<MyJsx1/>)
}else{
return(<MyJsx2/>)
}
}
You can chaage your component to:
render:function(){
return(
<div className="panel panel-default">
<div className="panel-heading">
{this.props.info.name}
<span className="pull-right text-uppercase delete-button" onClick={this.deleteSchool}>×</span>
</div>
{this.props.info = "nothing"?
(<div className="panel-body">{this.props.info.tagline}</div>)
:null}
</div>
)
} })
https://facebook.github.io/react/docs/conditional-rendering.html
Single line example
{(this.state.hello) ? <div>Hello</div> : <div>Goodbye</div>}
or
{(this.state.hello) ? <div>Hello</div> : false}
I often create an explicit function to to this, to avoid clutter in the main render:
var ChildComponent = React.createClass({
render: function () {
return (<p>I am the child component</p>)
}
});
var RootComponent = React.createClass({
renderChild: function () {
if (this.props.showChild === 'true') {
return (<ChildComponent />);
}
return null;
},
render: function () {
return(
<div>
{ this.renderChild() }
<p>Hello World!</p>
</div>
)
}
});
http://reactkungfu.com/2016/11/dynamic-jsx-tags/
For many React developers using JSX it is not clear how to make a
dynamic JSX tag. Meaning that instead of hardcoding whether it is
input or textarea or div or span (or anything else) we would like to
keep it in a variable.

Cannot interact between react components

This is for a react JS project (jsfiddle). The textbox should update with the true/false checked value of the checkbox, but it does not do so. Can someone explain why?
var AutoGenerateCheckbox = React.createClass ({
getInitialState: function() {
return {checked: false};
},
update() {
this.state.checked = !this.state.checked;
alert(this.state.checked);
this.props.onUpdate(this.state.checked);
},
render() {
return (
<input type="checkbox" checked={this.state.checked} onChange={this.update} />
);
}
});
var TBox = React.createClass({displayName: 'TextBox',
render: function() {
return (
<div>
Checkbox value: {this.props.data}
</div>
);
}
});
var KApp = React.createClass({
getInitialState: function() {
return {autoChecked: false};
},
handleAutogenChange: function(val) {
alert('handleAutogenChange:' + val);
this.setState({autoChecked : val});
},
render: function() {
return (
<div>
<AutoGenerateCheckbox onUpdate={this.handleAutogenChange}/>
<TBox data={this.state.autoChecked}/>
</div>
);
}
});
ReactDOM.render(
<KApp />,
document.getElementById('content')
);
The reason you don't see anything printed out is because you are trying to print a boolean value here
<div>
Checkbox value: {this.props.data}
</div>
try
<div>
Checkbox value: {this.props.data.toString()}
</div>
instead.
As an extra tip, you don't really need to hold the state of the checkbox in both its own state and its parent component's state. You really only need to have it in the parent component's state.
See the fiddle I made.
React is not determining the Boolean value to be printable information, try this instead:
<div>
Checkbox value: {this.props.data.toString()}
</div>

React.js insert into ref of another component

I'm starting to learn react.js, and I've face a situation that I can't get an answer.
This is my components structure(this is just to show hierarchy):
-- ItemContainer
----- ItemList
-------- ItemSingle
----- ItemForm
Where ItemList and ItemForm are siblings, chilren of ItemContainer. And ItemSingle is a child from ItemList.
On each ItemSingle I have a "edit" button, that should update the form with its content, but I'm not able to send from ItemSingle to ItemForm.refs.
This is what I've tried
ItemSingle Component:
var ItemSingle = React.createClass({
insertEdit: function(edit_item, e){
e.preventDefault();
React.findDOMNode(ItemForm.refs.title).value = edit_item.title;
React.findDOMNode(ItemForm.refs.content).value = edit_item.content;
},
render: function() {
return (
<div className="box">
<div className="box-body bigger-box">
<h4>
<strong>#{this.props.index + 1}</strong>
<span className="title">{this.props.title}</span>
<span className="float-right box-option">
<a href="#" onClick={this.insertEdit.bind(this, this.props)}>Edit</a>
</span>
</h4>
<div className="text" dangerouslySetInnerHTML={{__html: this.props.content}} />
</div>
</div>
);
}
});
ItemForm Component:
var ItemForm = React.createClass({
handleSubmit: function(e) {
e.preventDefault();
var title = React.findDOMNode(this.refs.title).value.trim();
var content = React.findDOMNode(this.refs.content).value.trim();
if(!title || !content){ return false; }
this.props.onItemsAdd({title: title, content: content});
React.findDOMNode(this.refs.title).value = '';
React.findDOMNode(this.refs.content).value = '';
$('textarea').trumbowyg('empty');
return true;
},
componentDidMount: function() {
$('textarea').trumbowyg();
},
render: function() {
return (
<div className="form-container">
<form className="form" method="POST">
<div className="form-group">
<input type="text" className="form-control" ref="title"/>
</div>
<div className="form-group">
<textarea ref="content"></textarea>
</div>
<div className="form-group">
<a className="btn btn-success btn-flat btn-block btn-lg" onClick={this.handleSubmit}><i className="fa fa-save"></i> Save</a>
</div>
</form>
</div>
);
}
});
And this is the error I got on ItemSingle line number 4:
Uncaught TypeError: Cannot read property 'title' of undefined
Refs in React are not meant to be used like this.
If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use refs to "make things happen" – instead, the data flow will usually accomplish your goal.
In your app you will need state, probably in ItemList component, and methods to change this state should be passed to the ItemSingle components.
https://facebook.github.io/react/docs/more-about-refs.html

How to render checked radio button in React

I came of with a solution for pre checked radio button based on JSON data but I think the code can be improved. Or there should be a better way to implement it. The data is structure in following way:
{
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C',
selected: 'Radio A'
}
Not sure if this is the best way to do it. The data structure can be modified if needed.
Based on the data provided I am rendering radio button with one of them pre-selected. Following is what I have.
var App = React.createClass({
getInitialState: function(){
return {
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C',
selected: 'Radio A'
}
},
render: function(){
return(
<div>
{Object.keys(this.state).map(function(key) {
// Not displaying the selected data
if(key !== 'selected'){
// Checked radio button
if(this.state[key] == this.state['selected']){
return(
<div>
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked value={key} /> {this.state[key]}
</label>
</div>
);
}
else{
return(
<div>
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" value={key} /> {this.state[key]}
</label>
</div>
);
}
}
}, this)}
</div>
);
}
});
React.render(<App />, document.getElementById('container'));
The code is set up following jsfiddle.com
https://jsfiddle.net/rexonms/2x7ey2L5/
Is there a better solution to implement this? Thanks in advance
There is indeed a better way. In your render method, the JSX inside the two branches of your if statement are identical, except that one has checked and the other doesn't. Repetition like that can—and should—almost always be cleaned up.
Note: This answer was written with React 0.14 in mind. To see the code as written with more modern React idioms, see this revision.
In the case of boolean attributes like checked in JSX, you can give them a truthy or falsy value to control whether or not the rendered HTML element has the attribute or not. In other words, if you have <input checked={10 > 1}/>, you'll get <input checked/>. If you have <input checked={10 > 100}/>, you'll get <input/>.
With that in mind, we can reduce your code to something like this:
var App = React.createClass({
getInitialState: function() {
return {
radios: {
radioA: 'Radio A',
radioB: 'Radio B',
radioC: 'Radio C'
},
selected: 'radioA'
}
},
renderRadioWithLabel: function(key) {
var isChecked = key === this.state.selected;
return (
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked={isChecked} value={key} />
{this.state.radios[key]}
</label>
);
},
render: function() {
return (
<div>
{Object.keys(this.state.radios).map(function(key) {
return this.renderRadioWithLabel(key);
}, this)}
</div>
);
}
});
React.render(<App />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container"></div>
As you can see, I've broken the logic that renders the radio buttons and their labels into its own function. You don't have to do that, but I find that it makes render functions with inner loops a lot easier to read, and makes your intent clearer.
Some people, myself included, would advocate breaking that logic out into its own component so you can have a stateful container component and stateless child components, which makes the whole thing much, much easier to test. Here's a good article on the topic. One more thing you might want to do is make the list of keys and labels in an array, since the order of an object's properties is not guaranteed in JavaScript. If you want to know what it might look like with those changes, take a look at this snippet:
var Radio = React.createClass({
render: function() {
var key = this.props.key;
return (
<label className="radio-inline" key={key} htmlFor={key}>
<input id={key} type="radio" checked={this.props.selected} value={key} />
{this.props.label}
</label>
);
}
});
var RadiosContainer = React.createClass({
getInitialState: function() {
return { selected: this.props.initialSelected };
},
render: function() {
return (
<div>
{this.props.radios.map(function(radioProps) {
var selected = this.state.selected === radioProps.key;
return <Radio {...radioProps} selected={selected} />;
}, this)}
</div>
);
}
});
var radios = [
{ key: 'radioA', label: 'Radio A' },
{ key: 'radioB', label: 'Radio B' },
{ key: 'radioC', label: 'Radio C' }
];
var selectedRadio = 'radioB';
React.render(
<RadiosContainer radios={radios} initialSelected={selectedRadio} />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container"></div>

Categories

Resources