I am creating one react form which looks like wizard type means on click of next button, it will load another component.
To do with this, i am using javascript iterators. I am updating state based on what iterator returns but i dont understand its not updating state of the component.
Code:
console.clear();
var Email = React.createClass({
render: function() {
return (
<div className="form-group">
<label className="control-label">Email:</label>
<input className="form-control" />
</div>
);
}
});
var Password = React.createClass({
render: function() {
return (
<div className="form-group">
<label className="control-label">Password:</label>
<input className="form-control" />
</div>
);
}
});
var Contact = React.createClass({
render: function() {
return (
<div className="form-group">
<label className="control-label">Contact:</label>
<input className="form-control" />
</div>
);
}
});
var Form = React.createClass({
getInitialState: function(){
this.fit = this.flowIterator([<Email />,<Password />,<Contact />]);
return {
view: this.fit.next(),
submitBtnText: "Next"
};
},
flowIterator: function(array){
var nextIndex = 0;
return {
next: function(){
console.log("nextIndex=",nextIndex);
console.log("array.length-1=",array.length-1);
console.log("nextIndex == array.length-1 =",nextIndex == array.length-1);
return nextIndex == array.length-1 ?
{value: array[nextIndex++], done: true} :
{value: array[nextIndex++], done: false};
}
}
},
onFormSubmit: function(e){
this.setState({
onChange: this.onStateChange()
});
e.preventDefault();
},
onStateChange: function(){
console.log("State change event fired!!");
this.setState({
view: this.fit.next(),
submitBtnText: "Next"
});
console.log("this.state.view.done=",this.state.view.done);
if(this.state.view.done){
this.setState({
submitBtnText: "Login"
});
}
},
render: function() {
return (
<form className="form-horizontal col-xs-6" onSubmit={this.onFormSubmit}>
{this.state.view.value}
<div className="form-group">
<button className="btn btn-success pull-right">{this.state.submitBtnText}</button>
</div>
</form>
);
}
});
ReactDOM.render(
<Form />,
document.getElementById('container')
);
check the code in jsfiddle
Thank you...
Related
I am new to react js.I have a problem in rendering the pretty json data inside textarea.I don't know Which part is wrong
I want my prettyjson to render inside textarea Like this
"email":"xxxx#x.com",
"email":"yyyy#y.com",
.....
This is my code
But I am getting Nothing inside my textarea
/**
* Created by arfo on 6/26/2016.
*/
var React =require('react');
var api = require('../utils');
var Bulkmail = React.createClass({
getInitialState:function () {
return{
default:10,
data:[],
color:'#58FA58'
}
},
componentDidMount:function () {
api.getemail(this.state.default).then(function (response) {
this.setState({
data:response
})
}.bind(this))
},
onSubmit:function (e) {
e.preventDefault();
console.log(this.refs.text.value.trim());
},
onChange:function (e) {
e.preventDefault();
//console.log(this.refs.text.value.trim())
var data = this.refs.text.value.trim();
if(isNaN(data)){
this.setState({
color:'#FE2E2E'
})
}else{
this.setState({
color:'#58FA58'
})
}
},
render:function () {
console.log(this.state.data);
var results = this.state.data;
return(
<div className="bodybox">
<div className="box">
<div className="upc">
<p>Generate Bulk Email</p>
<form onSubmit={this.onSubmit}>
<input onChange={this.onChange} type="text" style={{border:'1px solid '+this.state.color}} ref="text" defaultValue={this.state.default} placeholder="Enter Number"/>
<button>Get Data</button>
</form>
<div className="result">
<ul>
{this.state.data.map(function (data) {
return <li key={data.id}>{data.email}</li>
})}
</ul>
</div>
</div>
<div className="tdown">
<p>Json Format</p>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
<textarea defaultValue={this.state.data.map(function(data) {
return JSON.stringify(data.email)
})} >
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
</textarea>
</div>
</div>
</div>
)
}
});
module.exports = Bulkmail ;
No need to use difficult regex, we can use functionality from JSON.stringify(object, undefined, 2) to get beautifully rendered strings from JSON.
var obj={"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}
var pretty = JSON.stringify(obj, undefined, 2);
var ugly = document.getElementById('myTextArea').value; document.getElementById('myTextArea').value = pretty;
<textarea value={this.state.data.map(e=>JSON.stringify(e))} defaultValue="val" />
result {"email":"some#mail"},{"email":"some#mail"},{"email":"some#mail"}
let value = this.state.data.map(e=>JSON.stringify(e).replace(/{|}/g,''));
<textarea value={value} defaultValue="val" />
result "email" : "xx#yy.y", "email" : "some#mail", "email" : "some#mail"
let value = this.state.data.map(e=>JSON.stringify(e).replace(/{|}/g,'')).join(',\n');
<textarea value={value} defaultValue="val" />
result "email" : "xx#yy.y",
"email" : "some#mail",
"email" : "some#mail"
In HTML, the value of is set via children. In React, you should use value instead.
/**
* Created by arfo on 6/26/2016.
*/
var React =require('react');
var api = require('../utils');
var Bulkmail = React.createClass({
getInitialState:function () {
return{
default:10,
data:[],
color:'#58FA58'
}
},
componentDidMount:function () {
api.getemail(this.state.default).then(function (response) {
this.setState({
data:response
})
}.bind(this))
},
onSubmit:function (e) {
e.preventDefault();
console.log(this.refs.text.value.trim());
},
onChange:function (e) {
e.preventDefault();
//console.log(this.refs.text.value.trim())
var data = this.refs.text.value.trim();
if(isNaN(data)){
this.setState({
color:'#FE2E2E'
})
}else{
this.setState({
color:'#58FA58'
})
}
},
getEmailValue:function(){
return this.state.data.map(function(data) {
return JSON.stringify(data.email)
}).join('\n');
},
render:function () {
console.log(this.state.data);
var results = this.state.data;
return(
<div className="bodybox">
<div className="box">
<div className="upc">
<p>Generate Bulk Email</p>
<form onSubmit={this.onSubmit}>
<input onChange={this.onChange} type="text" style={{border:'1px solid '+this.state.color}} ref="text" defaultValue={this.state.default} placeholder="Enter Number"/>
<button>Get Data</button>
</form>
<div className="result">
<ul>
{this.state.data.map(function (data) {
return <li key={data.id}>{data.email}</li>
})}
</ul>
</div>
</div>
<div className="tdown">
<p>Json Format</p>
<textarea value={getEmailValue()}
</textarea>
</div>
</div>
</div>
)
}
});
JSFiddle: https://jsfiddle.net/morahood/cp569zg6/38/
When I delete a child ServiceItem component from the ServiceList component, I am running into an issue where the last ServiceItem in the list is removed instead of the targeted ServiceItem. The JSFiddle link above will help understand the issue, just make sure you enter in some identifiable information in the input fields so you can see what is getting deleted.
How do I fix my application so that the respective component is deleted?
var ServiceForm = React.createClass({
render: function() {
return (
<form onSubmit={ this.handleFormSubmission } >
{ this.renderServiceItemList() }
<div className="btn btn-default btn-sm" onClick={ this.addServiceItem }>+ Append New Service Item</div>
<button type="submit" className="btn btn-success btn-lg pull-right">Submit</button>
</form>
);
},
getInitialState: function() {
return ({
serviceItems: [this.renderServiceItem]
});
},
handleFormSubmission: function(event) {
event.preventDefault();
var data = this.refs.service_item_list.getAllServiceItems();
var json = {
"service_request" : {
"services" : data,
"additional_recipients" : 'test#example.com',
"comments" : 'Hello world!'
}
};
console.log(json);
},
addServiceItem: function(event) {
var copy = this.state.serviceItems.slice();
copy.push(this.renderServiceItem);
this.setState({
serviceItems: copy
});
},
renderServiceItem: function(item, i) {
return (
<ServiceItem removeServiceItem={ this.removeServiceItem } data={item} key={i} ref={"service_item_" + i} />
);
},
removeServiceItem: function(event) {
var index = parseInt(event.target.value, 10);
var copy = this.state.serviceItems.slice();
copy.splice(index, 1);
this.setState({
serviceItems: copy
});
},
renderServiceItemList: function() {
return (
<ServiceItemList serviceItems={ this.state.serviceItems }
renderServiceItem={ this.renderServiceItem }
removeServiceItem={ this.removeServiceItem }
ref="service_item_list" />
);
}
});
var ServiceItemList = React.createClass({
render: function() {
var content;
if (this.props.serviceItems.length < 1) {
content = <div className="empty-service-list">There are no service items, click on append new service item below!</div>;
} else {
content = this.props.serviceItems.map(this.props.renderServiceItem);
}
return (
<div>
{ content }
</div>
);
},
getAllServiceItems: function() {
var data = [];
for (var ref in this.refs) {
data.push(this.refs[ref].serializeItem());
}
var merged = [].concat.apply([], data);
return(merged);
}
});
var ServiceItem = React.createClass({
render: function() {
return (
<div className="row">
<div className="col-sm-3">
<div className="form-group service-item">
<label>Service Item </label>
<select multiple ref="service_types" className="form-control">
<option>Oil Change</option>
<option>Tire Rotation</option>
<option>New Wiper Blades</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Customer </label>
<select ref="customer" className="form-control">
<option>Troy</option>
<option>Dave</option>
<option>Brandon</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Manufacturer </label>
<div className="input-group">
<input ref="manufacturer" type="text" className="form-control" />
</div>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Model </label>
<div className="input-group">
<input ref="model" type="text" className="form-control" />
</div>
<a href="#" onClick={ this.props.removeServiceItem }>
<span aria-hidden="true" className="remove-fields" onClick={ this.props.removeServiceItem }>×</span>
</a>
</div>
</div>
</div>
);
},
getInitialState: function() {
return({
service_types: [],
customer: '',
manufacturer: '',
model: ''
});
},
serializeItem: function() {
var customer = this.refs.customer.value;
var manufacturer = this.refs.manufacturer.value;
var model = this.refs.model.value;
var selected = this.getSelectedServiceItems();
var services = this.getSelectedServiceItems().map(function(item) {
return (
{
"service" : {
"service_type" : item,
"customer" : customer,
"manufacturer" : manufacturer,
"model" : model
}
}
)
});
return(services);
},
getSelectedServiceItems: function() {
var select = this.refs.service_types;
var values = [].filter.call(select.options, function (o) {
return o.selected;
}).map(function (o) {
return o.value;
});
return(values);
}
});
ReactDOM.render(
<ServiceForm />,
document.getElementById('container')
);
Your issue is with your key={i}.
React lists requires the key of an item to be unique to the item, regardless of the position of the item in the list. This allows react to do smart management of the list in case of updates.
React will NOT render the following update correctly:
From: To:
name: key: name: key:
Bill 0 Bill 0
Charlie 1 Dave 1
Dave 2
Because react thinks that the Charlie record is unchanged (because the key is unchanged).
I would advise you to put some sort of ID from the service-item retrieved as key.
On name, e.g.
From: To:
name: key: name: key:
Bill Bill Bill Bill
Charlie Charlie Dave Dave
Dave Dave
React will render the update correctly in this case (because key is unique to the item).
I am still learning React and Javascript so thank you for your patience.
I am trying to serialize my form data so I can send it to a Ruby on Rails back end for processing. I am just using vanilla React with no additional depedencies like Flux, Redux, etc.
It seems like my child components are not returning anything and I am not quite sure why.
I have tried:
exposing values through the use of refs (but failed and read that it isn't really a good idea to do that anyway)
exposing a parent method within my child components to gather information about each individual component (what you will see in my jsfiddle).
updating component states through onChange methods and trying to access the states of each child component
My JSFiddle: https://jsfiddle.net/morahood/0ptdpfu7/91/
I am clearly missing a key element of React design patterns here. Am I just way off track? How can I get back on track? I would like to eventually be able to serialize the form data in the following format
{
"service_request" : {
"services" : [
{
"service_item" : ["Oil Change", "New Windshield Wipers"],
"customer" : "Troy",
"manufacturer" : "Ford",
"model" : "F150"
},
{
"service_item" : ["Tire Rotation"],
"customer" : "Dave",
"manufacturer" : "Hyundai",
"model" : "Genesis"
}
]
}
}
Components
var ServiceForm = React.createClass({
render: function() {
return (
<form onSubmit={ this.handleFormSubmission }>
{ this.state.serviceItems.map(function(item) {
return (item);
})}
<div className="btn btn-default btn-sm" onClick={ this.addServiceItem }>+ Append New Service Item</div>
<button type="submit" className="btn btn-success btn-lg pull-right">Submit</button>
</form>
);
},
getInitialState: function() {
return ({
serviceItems: [<ServiceItem serializeServiceItem={ this.serializeServiceItem } />]
});
},
handleFormSubmission: function() {
console.log("form submitted!");
alert("Serialized Form Data: " + this.serializeFormData());
},
addServiceItem: function(event) {
var serviceItems = this.state.serviceItems;
serviceItems.push(<ServiceItem serializeServiceItem={ this.serializeServiceItem } />);
this.setState({
serviceItems: serviceItems
});
},
serializeServiceItem: function() {
var jsonData = {
"service_item" : this.state.service_items,
"customer" : this.state.customer,
"manufacturer" : this.state.manufacturer,
"model" : this.state.model
}
return (jsonData);
},
serializeFormData: function() {
return( this.state.serviceItems.map(function(item) {
return (item.serializeServiceItem);
}));
}
});
var ServiceItem = React.createClass({
render: function() {
return (
<div className="row">
<div className="col-sm-3">
<div className="form-group">
<label>Service Item </label>
<select multiple name="service_item" selected={ this.state.service_items } className="form-control">
<option>Oil Change</option>
<option>Tire Rotation</option>
<option>New Wiper Blades</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Customer </label>
<select name="customer" selected={ this.state.customer } className="form-control">
<option>Troy</option>
<option>Dave</option>
<option>Brandon</option>
</select>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Manufacturer </label>
<div className="input-group">
<input name="manufacturer" value={ this.state.manufacturer } onChange={ this.setManufacturer } type="text" className="form-control" />
</div>
</div>
</div>
<div className="col-sm-3">
<div className="form-group">
<label>Model </label>
<div className="input-group">
<input name="model" value={ this.state.model } onChange={ this.setModel } type="text" className="form-control" />
</div>
</div>
</div>
</div>
);
},
getInitialState: function() {
return({
service_items: [],
customer: "",
manufacturer: "",
model: ""
});
},
setModel: function(event) {
this.setState({ model: event.target.value });
},
setManufacturer: function(event) {
this.setState({ manufacturer: event.target.value });
},
setCustomer: function(event) {
this.setState({ customer: event.target.selected });
},
setServiceItems: function(event) {
this.setState({ service_items: event.target.selected });
}
});
ReactDOM.render(
<ServiceForm />,
document.getElementById('container')
);
Solution
https://jsfiddle.net/morahood/cp569zg6/19/
You "might" be way overcomplicating things here. The DOM element for <form> can actually be treated as an array of all the inner <input> elements. In other words, if you have:
render: function() {
return (
<form ref="form">
...
</form>
);
}
All your input elements can be accessed by:
serialized = {}
for (var i in this.refs.form) {
var input = this.refs.form[i];
serialized[input.name] = input.value;
}
This might not provide you with enough flexibility. A better solution might be to define methods in your component instances that return the input values:
var ServiceForm = React.createClass({
serializeFormData: function() {
return {
foo: this.refs.foo.serialize()
};
},
render: function() {
var foo = this.state.foo;
return (
<ServiceItem data={foo} ref="foo" />
);
}
});
var ServiceItem = React.createClass({
serialize: function() {
return {
model: this.refs.model.value,
...
}
},
render: function() {
var model = this.props.data.model;
return (
<input ref="model" value={model} ... />
);
}
});
If you need multiple service items, you'll probably need to rely on this.props.children to access each component instance rather than on this.refs:
var ServiceContainer = React.createClass({
collectFormData: function() {
return this.refs.form.serialize();
},
renderServiceItem: function(item, i) {
return (
<ServiceItem data={item} key={i} />
);
},
render: function() {
// Assuming you've moved all your state logic into this ServiceContainer
var serviceItems = this.state.serviceItems;
return (
<ServiceForm ref="form">
{serviceItems.map(this.renderServiceItem)}
</ServiceForm>
);
}
});
var ServiceForm = React.createClass({
serialize: function() {
return React.Children.map(this.props.children, function(item) {
return item.serialize();
});
},
render: function() {
return (
<div>{this.props.children}</div>
);
}
});
var ServiceItem = React.createClass({
serialize: function() {
// You can still access your input elements through refs in here
...
},
render: function() {
...
}
});
Note that I'm using React.Children here rather than simply using this.props.children because when there's only a single child, children is not an array (see: https://facebook.github.io/react/tips/children-props-type.html).
You could use react.rb and reactive-record which will do all this for you out of the box. http://reactrb.org
Playing with react, trying to build a simple chat app that will be connected to firebase.
I have one container - ChatApp - and one component UserInput.
ChatApp ->
import React from 'react';
import UserInput from '../components/UserInput';
export default React.createClass({
getInitialState: function() {
return {
chatting: false
};
},
render: function() {
return <div>
<UserInput
startChat={this.chatCanStart}
/>
</div>;
},
chatCanStart: function(recipient) {
console.log(recipient);
this.setState({
chatting: true
})
}
});
UserInput ->
import React from 'react';
export default React.createClass({
getInitialState: function() {
return {
username: '',
recipient: 'asgasg'
};
},
handleUsernameChange: function(event) {
let text = event.target.text;
this.setState({
username: text
});
},
handleRecipientChange: function(event) {
let text = event.target.text;
this.setState({
recipient: text
});
},
handleStartChat: function() {
if (this.state.username !== '' && this.state.recipient !== ''){
console.log(this.state.recipient);
this.props.startChat(this.state.recipient);
}
},
render: function() {
return <div className="panel panel-default">
<div className="panel-heading"> Welcome to the chat app done in React </div>
<div className="panel-body">
<div className="input-group">
<span className="input-group-addon"> Username </span>
<input type="email" className="form-control" value={this.state.username} onChange={this.handleUsernameChange} />
</div>
<br />
<div className="input-group">
<span className="input-group-addon"> Recipient </span>
<input type="email" className="form-control" value={this.state.recipient} onChange={this.handleRecipientChange} />
</div>
<br />
<button type="button" className="btn btn-primary" onClick={this.handleStartChat}> Chat now </button>
</div>
</div>;
}
});
When I change the Username and Recipient fields and then click on the Chat now button, I am expecting the chatting state in the ChatApp container to change to true and also want to log the passed recipient.
But I get 'undefined', why is that? I get 'undefined' even in the console.log in the UserInput component. Even though the inputs value in the form is being changed just fine.
What am I doing wrong?
Thanks.
To get value from input you need to use .value property instead of .text
handleUsernameChange: function(event) {
let text = event.target.value;
this.setState({
username: text
});
},
handleRecipientChange: function(event) {
let text = event.target.value;
this.setState({
recipient: text
});
},
Example
I'm running into persistent problems in my React renders.
This code
/** #jsx React.DOM */
var AnswerRows = React.createClass({
componentDidMount: function() {
},
render: function() {
{this.props.answers.map(function(answer, i) {
return (
<div id="answerRow">
<label className="AnswerText">
<input type="checkbox" value={answer.id} />
{answer.text}
</label>
</div>
);
}, this)}
}
});
var QuizTaking = React.createClass({
componentDidMount: function() {
},
render: function() {
return (
<div className="card-holder">
<div className="showQuestionCard x-card host">
<h3 dangerouslySetInnerHTML={{__html: this.props.question.text}}></h3>
<div className="answerRows">
<AnswerRows answers={this.props.question.answers}/>
</div>
<div className='submitAnswers'></div>
</div>
<div className="paper-shadow-bottom"></div>
</div>
)
}
})
Will give me Invariant Violation: AnswerRows.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object. errors. However, this works every time:
/** #jsx React.DOM */
var TestIndex = React.createClass({
propTypes: {
},
loadTest: function(i) {
window.location.replace(this.props.tests[i].url.replace(".json", "/take"))
},
render: function () {
return(
<div className="testTable card-holder">
<div className="card-contents">
{this.props.tests.map(function(test, i) {
return (
<div className="testTableRow x-card host" key={test.id}>
<label className="__TITLE">{test.title}
<button onClick={this.loadTest.bind(test, i)} key={i} type="button" className="StartButton paper-button raisedButton">Start this test</button>
</label>
<div className="paper-shadow-bottom"></div>
</div>
);
}, this)}
</div>
</div>
)
}
});
I'd really like to understand what is going on here, and perhaps the 'right' way to do it.
You cannot return multiple elements (like the array returned by the map function) from the render function. Also, you're missing a return statement. Try this:
var AnswerRows = React.createClass({
componentDidMount: function() {
},
render: function() {
return (
<div>
{this.props.answers.map(function(answer, i) {
return (
<div id="answerRow">
<label className="AnswerText">
<input type="checkbox" value={answer.id} />
{answer.text}
</label>
</div>
);
}, this)}
</div>
);
}
});