why am i getting [object object] instead of an object - forms

I have a react container with a form inside it. The form contains three radio buttons. I want each value for each radio button input to be an object taken from an array of objects in my reducer. However, when I console.log the value of a radio button input, I get this:
[object Object]
I know that [object Object] is the default toString representation of an object in javascript, but how can I grab the actual object so I can use the information inside of it?
here is my code:
class NoteInput extends React.Component {
constructor(props) {
super(props);
this.state={
selectedValue: null,
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({selectedValue: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
console.log(this.state.selectedValue);
}
render() {
var inputs = this.props.locations.map((location, i) => {
return (
<div key={i}>
<input type="radio" id={i} name="location" value={location} />
<label htmlFor={'choice' + {i}}>{location.name}</label>
</div>
);
});
return (
<div>
<form onSubmit={this.handleSubmit} onChange={this.handleChange} >
{inputs}
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
and here is my reducer:
export default function() {
return [
{name: 'Safa Park', locationLat: '25.184992', locationLong: '55.248140'},
{name: 'Mercato', locationLat: '25.217054', locationLong: '55.253051'},
{name: 'Burj Khalifa', locationLat: '25.197787', locationLong: '55.274862'}
]
}

You can store the location object into stringify format as a value of radio button.
var inputs = this.props.locations.map((location, i) => {
let strLoc = JSON.stringify(location); // stringify it
return (
<div key={i}>
<input type="radio" id={i} name="location" value={strLoc} />
<label htmlFor={'choice' + { i }}>{location.name}</label>
</div>
);
});
In handleSubmit can get back in json/object format.
handleSubmit(e) {
e.preventDefault();
let strLoc = JSON.parse(this.state.selectedValue); //parse it back to json/object
console.log(strLoc.name);
}
Working codesandbox demo

Related

Can't clear form/state after input in React.js

I have a form which ultimately will be used as the UI to make some API calls to Open weather map.
Right now when I submit the a zip code in the input field, upon submission [object Object] propagates the field like in the screen shot below.
The call to the API is working as I am getting the JSON for the correct zip code...
But shouldn't this in the handleSubmit take care of everything i.e. using Object.assign to create new state and then using form.zipcode.value = ''; to clear out the input?
Thanks in advance!!
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
I have enclosed all of the component's code here.
import React, { Component } from 'react';
import * as api from '../utils/api';
import '../scss/app.scss';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
zipcode: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
zipcode: event.target.value,
});
}
handleSubmit(event) {
event.preventDefault();
var form = document.forms.weatherApp;
api.getWeatherByZip(this.state.zipcode).then(
function(zip) {
console.log('zip', zip);
this.setState(function() {
return {
zipcode: Object.assign({}, zip),
};
});
}.bind(this)
);
form.zipcode.value = '';
}
render() {
return (
<div className="container">
<form name="weatherApp" onSubmit={this.handleSubmit}>
<h2>Open Weather App</h2>
<div className="row">
<div className="one-half column">
<label htmlFor="insertMode">Insert your location</label>
<input
name="zipcode"
className="u-full-width"
placeholder="please enter your zipcode"
type="text"
autoComplete="off"
value={this.state.zipcode}
onChange={this.handleChange}
/>
</div>
<div className="one-half column">
<label htmlFor="showMin">show minimum</label>
<input type="checkbox" />
<label htmlFor="showMax">show maximum</label>
<input type="checkbox" />
<label htmlFor="showMean">show mean</label>
<input type="checkbox" />
</div>
</div>
<div className="row">
<div className="two-half column">
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
);
}
}
You should let react manage the changes to the DOM rather that editing it manually. As the value of your input field is already bound to this.state.zipcode to reset it just invoke this.setState({zipcode: ''}) instead of form.zipcode.value='';.

Reactjs submit form and setState not working first time

I have a submit action for my form which basically validates on submit.
It is working as i expect because when i submit the form it renders the errors. But the issue occurs when i do the submit i do not want to do the ajax request as the form is invalid. I notice that on the first submit the emailError is not set (default) but the second submit the state contains the correct emailError set to true.
I understand from the react docs that setState is not available immeditely as it is pending.
How can i get around this issue?
My code is below
import React, { Component } from 'react';
import isEmail from 'validator/lib/isEmail';
class formExample extends Component
{
constructor(props) {
super(props);
this.state = {
email: '',
emailError: false
};
this.register = this.register.bind(this);
this.updateState = this.updateState.bind(this);
}
updateState(e) {
this.setState({ email: e.target.value });
}
validateEmail() {
if (!isEmail(this.state.email)) {
console.log("setting state");
this.setState({ emailError: true });
return;
}
console.log(this.state);
this.setState({ emailError: false });
}
register(event) {
event.preventDefault();
this.validateEmail();
//only if valid email then submit further
}
render() {
return (
<div className="row">
<div className="col-md-2 col-md-offset-4"></div>
<div className="col-lg-6 col-lg-offset-3">
<form role="form" id="subscribe" onSubmit={this.register}>
<div className="form-group">
<input type="text" className="form-control" placeholder="Email..." name="email" value={this.state.email} onChange={this.updateState} />
<div className="errorMessage">
{this.state.emailError ? 'Email address is invalid' : ''}
</div>
</div>
<div className="input-group input-group-md inputPadding">
<span className="input-group-btn">
<button className="btn btn-success btn-lg" type="submit">
Submit
</button>
</span>
</div>
</form>
</div>
</div>
);
}
}
export default formExample;
in register you call validateEmail, but not return anything, so the rest of the function get's called.
setState is async! so you cannot count on it in the rest of register.
Try this:
validateEmail() {
const isEmailError = !isEmail(this.state.email)
this.setState({ emailError: isEmailError });
return isEmailError;
}
register(event) {
if(this.validateEmail()){
//ajax
};
}
other approach will be:
validateEmail(ajaxCb) {
const isEmailError = !isEmail(this.state.email)
this.setState({ emailError: isEmailError }, ajaxCb);
}
register(event) {
function ajaxCb(){...}
this.validateEmail(ajaxCb)
}

React updating state in two input fields from form submission

I am trying to make a simple contact form using React. Eventually I will send the data collected from the state to a database, but for right now I am trying to just get it to console log the correct values.
Right now, the email field overrides the name field and when I console log both states, name shows up and email is undefined. Here is my React Component
import React, { Component, PropTypes } from 'react';
import ContactData from '../data/ContactData.js';
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
email: '',
textArea: ''
}
}
handleChange(event) {
event.preventDefault();
this.setState({
name: event.target.value,
email: event.target.email
})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.name + ' ' + this.state.email);
}
render() {
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" placeholder="Name" value={this.state.name} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" placeholder="Email" value={this.state.email} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
FormContact.PropTypes = {
subName: PropTypes.string,
subEmail: PropTypes.string
}
FormContact.defaultProps = {
subName: 'Sam',
subEmail: ''
}
class Contact extends Component {
render() {
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact />
</div>
)
}
}
export default Contact;
If I understand what you want, you could do it as follows :
Add an empty object in your state for the form values
formValues: {}
Add the name attribute to your fields
<input name="name" .... />
<input name="email" .... />
then depending on that name update your state in handleChange function
let formValues = this.state.formValues;
let name = event.target.name; // Field name
let value = event.target.value; // Field value
formValues[name] = value;
this.setState({formValues})
And if the values go one level deeper, you could use
value={this.state.formValues["name"]} instead of value={this.state.name} - where name is the value of the name attribute of your input field
Thus, everything together should be as follows :
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
formValues: {}
}
}
handleChange(event) {
event.preventDefault();
let formValues = this.state.formValues;
let name = event.target.name;
let value = event.target.value;
formValues[name] = value;
this.setState({formValues})
}
handleSubmit(event) {
event.preventDefault();
console.log(this.state.formValues);
}
render(){
return (
<form onSubmit={this.handleSubmit.bind(this)}>
<label> Name:
<input type="text" name="name" placeholder="Name" value={this.state.formValues["name"]} onChange={this.handleChange.bind(this)} />
</label><br />
<label> Email:
<input type="text" name="email" placeholder="Email" value={this.state.formValues["email"]} onChange={this.handleChange.bind(this)}/>
</label><br />
<input className="btn btn-primary" type="submit" value="Submit" />
</form>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is fiddle.
Hope this helps.
The reference to event.target.email does not exist on the event element. The value of a text input from an inline-event handler would be event.target.value for both email and name. The quick solution is to create a separate handler for each input:
handleChangeName(event) {
event.preventDefault();
this.setState({ name: event.target.value }); //<-- both use the same reference
} // to get the contextual value
handleChangeEmail(event) { // from the inputs v
event.preventDefault(); // |
this.setState({ email: event.target.value }); //<--------------------
}

React checkbox. DRY out onChange function to handle all checkboxes

In the following code I have two checkboxes. On-click, they change the state of the component to their respective values.
I am building a form that will need over 100 checkboxes and I don't want to write the "onChange" function for each checkbox.
Is there a way that I can write one OnChange function that will take a parameter, then set the state to that parameter?
I've tried many ways but this is still blocking me.
Thank you!
import React from 'react';
export default class InputSearch extends React.Component {
constructor(props) {
super(props);
this.state = {
inputInternship: '',
inputMidLevel: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
this.onChangeInternship = this.onChangeInternship.bind(this);
this.onChangeMidLevel = this.onChangeMidLevel.bind(this);
}
handleSubmit(e) {
e.preventDefault();
this.props.getJobData(this.state);
}
onChangeInternship(e) {
this.setState({
inputInternship: !this.state.inputInternship,
});
this.state.inputInternship == false? this.setState({ inputInternship: e.target.value }) : this.setState({ inputInternship: '' })
}
onChangeMidLevel(e) {
this.setState({
inputMidLevel: !this.state.inputMidLevel,
});
this.state.inputMidLevel == false? this.setState({ inputMidLevel: e.target.value }) : this.setState({ inputMidLevel: '' })
}
render() {
return (
<div className="search-form">
<form onSubmit={this.handleSubmit}>
<input type="checkbox" value="level=Internship&" checked={this.state.inputInternship} onChange={this.onChangeInternship} /> Internship <br />
<input type="checkbox" value="level=Mid+Level&" checked={this.state.inputMidLevel} onChange={this.onChangeMidLevel} /> Mid Level <br />
<div>
<button
type="submit"
>Search
</button>
</div>
</form>
</div>
);
}
}
You need to create a function that would return specific onChange function:
import React from 'react';
export default class InputSearch extends React.Component {
constructor(props) {
...
this.onChange = this.onChange.bind(this);
}
...
onChange(fieldName) {
return (event) => {
this.setState({
[fieldName]: !this.state[fieldName],
});
if (this.state[fieldName]) {
this.setState({ [fieldName]: '' })
} else {
this.setState({ [fieldName]: e.target.value })
}
}
}
...
render() {
return (
<div className="search-form">
<form onSubmit={this.handleSubmit}>
<input type="checkbox" value="level=Internship&" checked={this.state.inputInternship} onChange={this.onChange('inputInternship')} /> Internship <br />
<input type="checkbox" value="level=Mid+Level&" checked={this.state.inputMidLevel} onChange={this.onChange('inputMidLevel')} /> Mid Level <br />
<div>
<button
type="submit"
>Search
</button>
</div>
</form>
</div>
);
}
}
By the way, are what is the point of changing state twice in your onChangeXYZ functions?
{
this.setState({
[fieldName]: !this.state[fieldName],
});
if (this.state[fieldName]) {
this.setState({ [fieldName]: '' })
} else {
this.setState({ [fieldName]: e.target.value })
}
}

render data inserted to collection in react component

My entire application is built on different react classes and displayed like this:
MainLayout = React.createClass({
render() {
return (
<div id="body">
<Header />
<main className="container">{this.props.content}</main>
<Footer />
</div>
);
}
});
All my front-end is built in react classes like the one below:
InsertData = React.createClass({
insertToCollection(event) {
event.preventDefault();
console.log(this.state.message + " state med message");
var content = Posts.find().fetch();
Posts.insert({
Place: $("post1").val(),
Type: $("post2").val(),
dateAdded: new Date(),
});
},
handleChange(event) {
this.setState({
message: event.target.value
})
console.log(this.state + " mer state her");
function insert(event) {
event.preventDefault();
console.log("added stuff");
}
},
render() {
return (
<div>
<form onSubmit={this.insertToCollection}>
<input type='text' placeholder="Select a restaurant" className="input-field"
onChange={this.handleChange} id="post1"/>
<input type='text' placeholder="What type of food they have" className="input-field"
onChange={this.handleChange} id="post2"/>
<button className="waves-effect waves-light btn btn-block" onChange={this.insert}> Submit </button>
</form>
<DisplayData />
</div>
);
}
});
Insert data to my collection works fine. I would like to render the inserted data onto the page from the <DisplayData /> component:
DisplayData = React.createClass({
render(){
var posts = Posts.find().fetch();
var postList = posts.map(function(posts){
return posts;
})
return <p> Your collection </p>
}
});
I'm rather stuck here, and not really sure how to iterate through the collection and render it in a list-structure for example. Here is my collection so far:
Posts = new Mongo.Collection('posts');
Posts.allow({
insert: function(){
return true;
},
update : function(){
return true;
},
remove : function(){
return true;
}
});
Here is a demo of how you can approach this: http://codepen.io/PiotrBerebecki/pen/bwmAvJ
I'm not sure about the format of your posts collection, but assuming that it is just a regular array, for example var posts = ['One', 'Two'];, you can render the individual post as follows:
var DisplayData = React.createClass({
render(){
var posts = ['One', 'Two'];
var renderPosts = posts.map(function(post, index) {
return (
<li key={index}>{post}</li>
);
});
return (
<div>
<p> Your collection </p>
<ul>
{renderPosts}
</ul>
</div>
);
}
});
Here is the full code from my codepen.
var InsertData = React.createClass({
insertToCollection(event) {
event.preventDefault();
console.log(this.state.message + " state med message");
var content = Posts.find().fetch();
Posts.insert({
Place: $("post1").val(),
Type: $("post2").val(),
dateAdded: new Date(),
});
},
handleChange(event) {
this.setState({
message: event.target.value
})
console.log(this.state + " mer state her");
function insert(event) {
event.preventDefault();
console.log("added stuff");
}
},
render() {
return (
<div>
<form onSubmit={this.insertToCollection}>
<input type='text' placeholder="Select a restaurant" className="input-field"
onChange={this.handleChange} id="post1"/>
<input type='text' placeholder="What type of food they have" className="input-field"
onChange={this.handleChange} id="post2"/>
<button className="waves-effect waves-light btn btn-block" onChange={this.insert}> Submit </button>
</form>
<DisplayData />
</div>
);
}
});
var DisplayData = React.createClass({
render(){
var posts = ['One', 'Two'];
var renderPosts = posts.map(function(post, index) {
return (
<li key={index}>{post}</li>
);
});
return (
<div>
<p> Your collection </p>
<ul>
{renderPosts}
</ul>
</div>
);
}
});
ReactDOM.render(
<InsertData />,
document.getElementById('app')
);
You need to pass posts to your view component DisplayData as props, so in this case after you inserted a post, you should update your state in the InsertData component. Actually it would be better if you do the insertion login inside a service rather than the component itself, but for simplicity right now you can check the following code:
InsertData = React.createClass({
getInitialState: function() {
return {posts: []}; // initialize the state of your component
},
insertToCollection(event) {
event.preventDefault();
console.log(this.state.message + " state med message");
var content = Posts.find().fetch();
Posts.insert({
Place: $("post1").val(), // better to retrieve these values from state. You can use `handleChange` method to keep track of user inputs
Type: $("post2").val(),
dateAdded: new Date(),
}, function(err, data){
var posts = this.state.posts || [];
posts.push(data);
this.setState({posts: posts}); //after setting the state the render method will be called again, where the updated posts will be rendered properly
});
},
handleChange(event) {
this.setState({
message: event.target.value
})
console.log(this.state + " mer state her");
function insert(event) {
event.preventDefault();
console.log("added stuff");
}
},
render() {
return (
<div>
<form onSubmit={this.insertToCollection}>
<input type='text' placeholder="Select a restaurant" className="input-field"
onChange={this.handleChange} id="post1"/>
<input type='text' placeholder="What type of food they have" className="input-field"
onChange={this.handleChange} id="post2"/>
<button className="waves-effect waves-light btn btn-block" onChange={this.insert}> Submit </button>
</form>
<DisplayData posts={this.state.posts}/>
</div>
);
}
});
var DisplayData = React.createClass({
render(){
var posts = this.props.posts || [];
var renderPosts = posts.map(function(post, index) {
return (
<li key={index}>{post}</li>
);
});
return (
<div>
<p> Your collection </p>
<ul>
{renderPosts}
</ul>
</div>
);
}
});