I have a child component with a select form element, this queries my API and makes a select box out of the data. I then try to pass the option back that's been selected via an OnChange function to my parent component so I can then send my data back to the server. I keep getting an error saying state is not defined, I am new to react and can't see where I am going wrong.
Here is my parent component
var ReactDom = require('react-dom');
const uuid = require('uuid/v1');
import {postDataTest} from "../actions/postData";
import TeamSelectBox from "./TeamSelectBox";
import React, {Component, PropTypes} from "react";
class PlayerForm extends Component {
constructor(props) {
super(props);
this.state = {
teamId: ''
};
this.handleChange = this.handleChange.bind(this);
}
fieldValues = {
name: null,
teamName: null,
bio: null
}
handleChange(dataFromChild) {
console.log(dataFromChild);
}
nextStep(e) {
e.preventDefault();
// Get values via this.refs
var player = {
id: uuid(),
name: ReactDom.findDOMNode(this.refs.name).value,
teamName: ReactDom.findDOMNode(this.refs.teamName).value,
bio: ReactDom.findDOMNode(this.refs.bio).value,
teamId: ReactDom.findDOMNode(this.refs.teamId).value
};
postDataTest(player);
}
render() {
return (
<div className="row">
<div className="col-md-6">
<div className="panel">
<div className="panel-heading">
<h1>Add Player</h1>
</div>
<div className="panel-body">
<form className="form-horizontal">
<div className="form-group">
<label className="control-label">Name</label>
<input type="text" className="form-control" ref="name" defaultValue={this.fieldValues.name}/>
</div>
<div className="form-group">
<label className="control-label">Team Name</label>
<input type="text" className="form-control" ref="teamName" defaultValue={this.fieldValues.teamName}/>
</div>
<TeamSelectBox state={this.state.teamId} onChange={this.handleChange}/>
<div className="form-group">
<label className="control-label">Bio</label>
<input type="textarea" className="form-control" ref="bio" defaultValue={this.fieldValues.bio}/>
</div>
<div className="bs-component">
<button className="btn btn-md btn-default btn-block" onClick={this.nextStep}>Save & Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}
module.exports = PlayerForm;
And here is my child form select box
import React, {Component} from "react";
import axios from "axios";
import {postDataTest} from '../actions/postData';
class TeamSelectBox extends Component {
constructor(props) {
super(props);
this.state = {
teams: []
};
}
componentDidMount() {
axios.get("/api/teams")
.then(response => {
const teams = response.data;
this.setState({teams});
console.log(teams);
});
}
render() {
return (
<div className="form-group">
<label for="inputSelect" className="control-label">Select Team</label>
<div className="bs-component">
<select value={this.probs.state.teamId} onChange={this.probs.onChange} className="form-control">
<option value=""></option>
{this.state.teams.map(singleTeam =>
<option value={singleTeam.id}>{singleTeam.team.name}</option>
)}
</select>
</div>
</div>
);
}
}
export default TeamSelectBox;
Your approach seems OK, but you had a typo in your child Component:
{this.probs.onChange}
change it to {this.props.onChange} and try again!
i think you have a typo, you missed spell props, this.props not this.probs
Related
I have blockchain application and created Reactjs interface to send data from user to application but when i enter data and try send it get error as follow , any one can help me?
The error is :
XHR POST http://localhost:3000/api/MedicalRecord
[HTTP/1.1 422 Unprocessable Entity 19ms]
The App.js file as follow
import React, { Component } from 'react';
import PostForm from './PostForm'
function App() {
return (
<div className="App">
<PostForm></PostForm>
</div>
);
}
export default App;
The PostForm.js file as follow
import React, { Component } from 'react'
import axios from 'axios'
class PostRecords extends Component {
constructor(props){
super(props)
this.state = {
record_Id: "",
patient: "",
doctor: "",
description: "",
prescription: ""
}
}
handleChange =(e) =>{
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault()
axios.post('http://localhost:3000/api/MedicalRecord')
.then(response =>{
console.log(response)
})
}
render() {
const {record_id, patient, doctor, description, prescription} = this.state
return (
<div>
<form onSubmit={this.handleSubmit}>
<div>
<label>record_id :</label>
<input
type='text'
name ='record_id'
value={record_id}
onChange={this.handleChange}
></input>
</div>
<div>
<label>patient :</label>
<input
type='text'
name ='patient'
value={patient}
onChange={this.handleChange}
></input>
</div>
<div>
<label>doctor :</label>
<input
type='text'
name ='doctor'
value={doctor}
onChange={this.handleChange}
></input>
</div>
<div>
<label>description :</label>
<input
type='text'
name ='description'
value={description}
onChange={this.handleChange}
></input>
</div>
<div>
<label>prescription :</label>
<input
type='text'
name ='prescription'
value={prescription}
onChange={this.handleChange}
></input>
</div>
<button type='submit'>Submit Now</button>
</form>
</div>
)
}
}
export default PostRecords
this is how components are look like, i have 3 component that involves in this task, whenever i choose diffrent select dropdown option which is in filter component, change method in parent componet gonna fired and that gonna call another method also in parent component which filterData. its filtering the data but some how after setting state i am not getting filtered data in child component. so i can show filterd data
//parent component
import React, { Component } from 'react';
import ListingsData from '../data/data';
import Listings from '../listings/listings.component';
import Filter from '../filters/filter.component';
import './main_content.styles.scss';
class Main_content extends Component {
constructor(props) {
super();
this.state = {
listingsData: ListingsData,
searchOnChange: '',
filterdProperties: ListingsData,
}
}
change = (e) => {
this.setState({
[e.target.name] : e.target.value
}, () => {
this.filterData();
}
)
}
filterData = () => {
let newData = this.state.listingsData.filter(property => {
return property.type.toLowerCase().toString() === this.state.houseType.toLowerCase().toString()
})
this.setState({ filterdProperties: newData }, () => {
console.log(this.state)
});
}
render() {
return (
<div className='main-content'>
<div className='listings-container'>
<Listings ListingsData={ this.state.filterdProperties } />
</div>
<div className='filter-sidebar'>
<Filter filterTypeHouse={this.change} />
</div>
</div>
);
}
}
export default Main_content;
//child component Listing.component.jsx
import React, {Component} from 'react';
import './listings.styles.scss';
import Listing from '../listing/listing.component'
class Listings extends Component {
constructor(props) {
super(props);
this.state = {
listings: props.ListingsData
}
}
render() {
if (this.state.listings === undefined || this.state.listings.length === 0) {
return <h2>sorry no data found</h2>
}
return (
<div className='listings'>
{this.state.listings.map(({ id, image, address, price, city, rooms, bathrooms, area }) => (
<Listing key={id} image={image} address={address} price={price} city={city} rooms={rooms} bathrooms={bathrooms} area={area} />
))}
</div>
)
}
}
export default Listings;
//child component filter.component.jsx
import React from 'react';
import './filter.styles.scss';
let Filter = (props) => (
<div className='filter'>
<div className='type'>
<label htmlFor='propertyType'>Type</label>
<select className='propertyType' name='houseType' onChange={props.filterTypeHouse} >
<option value='any'>Any</option>
<option value='family-house'>Family House</option>
<option value='single-house'>Single house</option>
<option value='apartment'>Apartment</option>
<option value='villa'>Villa</option>
<option value='office-building'>Office Building</option>
<option value='condo'>Condo</option>
</select>
</div>
<div className='location'>
<label htmlFor='PropertyLocation'>Location</label>
<select className='PropertyLocation'>
<option>Any</option>
<option>New york</option>
<option>California</option>
<option>Washington</option>
<option>philedelphia</option>
<option>Boston</option>
</select>
</div>
<div className='min-price'>
<label htmlFor='priceFrom'>Min-Price</label>
<input type='text' className='priceFrom' placeholder='min-price' />
</div>
<div className='max-price'>
<label htmlFor='priceTo'>Max-Price</label>
<input type='text' className='priceTo' placeholder='max-price' />
</div>
<div className='dealOption'>
<label htmlFor='options'>Type Of Deal</label>
<div className='options'>
<div className='each_option'>
<label htmlFor='any'>Any</label>
<input type='radio' name='dealType' className='any' value='any' />
</div>
<div className='each_option'>
<label htmlFor='sale'>Sale</label>
<input type='radio' name='dealType' className='sale' value='sale' />
</div>
<div className='each_option'>
<label htmlFor='rent'>Rent</label>
<input type='radio' name='dealType' className='rent' value='rent' />
</div>
</div>
</div>
</div>
)
export default Filter;
The component that you use for rendering the filtered lists (Listings) is rendering based on state as per below code.
constructor(props) {
super(props);
this.state = {
listings: props.ListingsData
}
}
The constructor is called only once when the component is mounted. So, the state will never get modified from the initial state, even if you pass new filtered props. To avoid this behaviour, use the props to display the listings data.
So you can change your Listings component to render from props.
class Listings extends React.Component {
render() {
const {ListingData:listings} = this.props;
if (listings === undefined || listings.length === 0) {
return <h2>sorry no data found</h2>
}
return (
<div className='listings'>
{listings.map(({ id, ...props}) => (
<div key={id}>
{JSON.stringify(props)}
</div>
))}
</div>
)
}
}
Try out the below SO snippet. Change the dropdown to apartment, villa, condo to see the filtered data being rendered.
const ListingsData = [
{ type: "apartment", location: "New york" },
{ type: "apartment", location: "New york" },
{ type: "apartment", location: "New york" },
{ type: "villa", location: "New york" },
{ type: "condo", location: "New york" },
{ type: "condo", location: "Washington" }
];
let Filter = props => {
return (
<div className="filter">
<div className="type">
<label htmlFor="propertyType">Type</label>
<select
className="propertyType"
name="houseType"
onChange={props.filterTypeHouse}
>
<option value="any">Any</option>
<option value="family-house">Family House</option>
<option value="single-house">Single house</option>
<option value="apartment">Apartment</option>
<option value="villa">Villa</option>
<option value="office-building">Office Building</option>
<option value="condo">Condo</option>
</select>
</div>
<div className="location">
<label htmlFor="PropertyLocation">Location</label>
<select className="PropertyLocation">
<option>Any</option>
<option>New york</option>
<option>California</option>
<option>Washington</option>
<option>philedelphia</option>
<option>Boston</option>
</select>
</div>
<div className="min-price">
<label htmlFor="priceFrom">Min-Price</label>
<input type="text" className="priceFrom" placeholder="min-price" />
</div>
<div className="max-price">
<label htmlFor="priceTo">Max-Price</label>
<input type="text" className="priceTo" placeholder="max-price" />
</div>
<div className="dealOption">
<label htmlFor="options">Type Of Deal</label>
<div className="options">
<div className="each_option">
<label htmlFor="any">Any</label>
<input type="radio" name="dealType" className="any" value="any" />
</div>
<div className="each_option">
<label htmlFor="sale">Sale</label>
<input type="radio" name="dealType" className="sale" value="sale" />
</div>
<div className="each_option">
<label htmlFor="rent">Rent</label>
<input type="radio" name="dealType" className="rent" value="rent" />
</div>
</div>
</div>
</div>
);
}
class Listings extends React.Component {
render() {
const {ListingData:listings} = this.props;
if (listings === undefined || listings.length === 0) {
return <h2>sorry no data found</h2>
}
return (
<div className='listings'>
{listings.map(({ id, ...props}) => (
<div key={id}>
{JSON.stringify(props)}
</div>
))}
</div>
)
}
}
class Main_content extends React.Component {
constructor(props) {
super();
this.state = {
listingsData: ListingsData,
searchOnChange: '',
filterdProperties: ListingsData,
}
}
change = (e) => {
console.log("Calling change handler");
this.setState({
[e.target.name] : e.target.value
}, () => {
this.filterData();
}
)
}
filterData = () => {
console.log("Calling filter data");
let newData = this.state.listingsData.filter(property => {
return property.type.toLowerCase().toString() === this.state.houseType.toLowerCase().toString()
})
this.setState({ filterdProperties: newData }, () => {
console.log("State", this.state);
});
}
render() {
return (
<div className="main-content">
<div className="listings-container">
<Listings ListingData={this.state.filterdProperties} />
</div>
<div className="filter-sidebar">
<Filter filterTypeHouse={this.change} />
</div>
</div>
);
}
}
ReactDOM.render(<Main_content />,document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This question already has answers here:
ReactJS this.state null
(5 answers)
Closed 6 years ago.
I have a component in react that is a simple form, I have a function that looks if there is a change in any of the form elements and then sets the state of the form element being edited. In the handleChange function when I do console.log(this.state) I see the exact thing I expect, the correct keys eg (name, teamName etc) with the values in I have entered in the form.
However when I click submit and it calls the function nextStep I get an error message saying this.state.name is null, am I missing something here?
Here is my component.
var ReactDom = require('react-dom');
const uuid = require('uuid/v1');
import {postDataTest} from "../actions/postData";
import TeamSelectBox from "./TeamSelectBox";
import React, {Component, PropTypes} from "react";
class PlayerForm extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
teamName: '',
bio: '',
teamId: ''
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(name, event) {
this.setState({[name]: event.target.value});
console.log(this.state);
}
nextStep(e) {
e.preventDefault();
// Get values via this.refs
var player = {
id: uuid(),
name: this.state.name,
teamName: this.state.teamName,
bio: this.state.bio,
teamId: this.state.teamId
};
postDataTest(player);
}
render() {
return (
<div className="row">
<div className="col-md-6">
<div className="panel">
<div className="panel-heading">
<h1>Add Player</h1>
</div>
<div className="panel-body">
<form className="form-horizontal">
<div className="form-group">
<label className="control-label">Name</label>
<input type="text" className="form-control" ref="name" defaultValue={this.state.name} onChange={this.handleChange.bind(this, 'name')}/>
</div>
<div className="form-group">
<label className="control-label">Team Name</label>
<input type="text" className="form-control" ref="teamName" defaultValue={this.state.teamName} onChange={this.handleChange.bind(this, 'teamName')}/>
</div>
<TeamSelectBox state={this.state.teamId} onChange={this.handleChange.bind(this, 'teamId')}/>
<div className="form-group">
<label className="control-label">Bio</label>
<input type="textarea" className="form-control" ref="bio" defaultValue={this.state.bio} onChange={this.handleChange.bind(this, 'bio')}/>
</div>
<div className="bs-component">
<button className="btn btn-md btn-default btn-block" onClick={this.nextStep}>Save & Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}
module.exports = PlayerForm;
setState doesn't mutate the state immediately and hence you must make use of callback in setState to log the updated value
handleChange(name, event) {
this.setState({[name]: event.target.value}, function() {
console.log(this.state);
});
}
Also you did not bind the nextStep function. You should do that in the constructor or any other way you prefer
class PlayerForm extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
teamName: '',
bio: '',
teamId: ''
};
this.handleChange = this.handleChange.bind(this);
this.nextStep = this.nextStep.bind(this);
}
handleChange(name, event) {
this.setState({[name]: event.target.value});
console.log(this.state);
}
nextStep(e) {
e.preventDefault();
// Get values via this.refs
var player = {
id: "9879",
name: this.state.name,
teamName: this.state.teamName,
bio: this.state.bio,
teamId: this.state.teamId
};
postDataTest(player);
}
render() {
return (
<div className="row">
<div className="col-md-6">
<div className="panel">
<div className="panel-heading">
<h1>Add Player</h1>
</div>
<div className="panel-body">
<form className="form-horizontal">
<div className="form-group">
<label className="control-label">Name</label>
<input type="text" className="form-control" ref="name" defaultValue={this.state.name} onChange={this.handleChange.bind(this, 'name')}/>
</div>
<div className="form-group">
<label className="control-label">Team Name</label>
<input type="text" className="form-control" ref="teamName" defaultValue={this.state.teamName} onChange={this.handleChange.bind(this, 'teamName')}/>
</div>
<div className="form-group">
<label className="control-label">Bio</label>
<input type="textarea" className="form-control" ref="bio" defaultValue={this.state.bio} onChange={this.handleChange.bind(this, 'bio')}/>
</div>
<div className="bs-component">
<button className="btn btn-md btn-default btn-block" onClick={this.nextStep}>Save & Continue</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<PlayerForm/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>
<div id="app"></div>
I am learning React and don't quite understand how to dynamically render components based on props.
For practice, I'm building a credit card validator which works well. Once the information is valid, I want to render my component to display a thank you message. I've added a property "mode" that contains "default". My thinking is that this prop would then change to "thankYouScreen" when the credit card info is validated. When this prop changes to "thankYouScreen" it would render the ThankYouMessage component.
Is this how one would go about this?
Components
import React from 'react';
import PaymentStore from './../store/paymentStore';
class InitialPaymentScreen extends React.Component {
constructor(props) {
super(props)
}
render() {
console.log(this.props);
return (
<div className="pay-form">
<div className="pay-form-wrapper">
<div className="pay-form-title">Payment Information</div>
<form className="payment-form js-error-hook" onSubmit={this.props.store.init.bind(this.props.store)}>
<div className="payment-form-wrap payment-form-cc-name">
<label className="payment-form-label">Name on Card</label>
<input className="payment-form-input js-cc-name" maxLength="50" placeholder="Enter your name..." type="text"/>
</div>
<div className="payment-form-wrap payment-form-cc">
<label className="payment-form-label">Card Number</label>
<input className="payment-form-input js-cc-number" placeholder="1234 5678 9012 3456" type="number"/>
</div>
<div className="payment-form-wrap payment-form-exp">
<label className="payment-form-label">Expiration</label>
<input className="payment-form-input js-cc-expiration" placeholder="04/17" type="number" min="0"/>
</div>
<div className="payment-form-wrap payment-form-cvv">
<label className="payment-form-label">CVV</label>
<input className="payment-form-input js-cc-cvv" placeholder="123" type="number"/>
</div>
<input type="submit" className="payment-form-submit" onClick={this.props.store.init.bind(this.props.store)} value="Next" />
</form>
</div>
</div>
);
}
}
class ThankYouMessage extends React.Component {
constructor(props) {
super(props)
}
render() {
return(
<div>Thanks!</div>
);
}
}
class PaymentForm extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<section className="main-content payment-form-wrapper">
<InitialPaymentScreen store={this.props.route.store} mode="default"/>
</section>
);
}
}
Store
let PaymentStore = {
validateFields() {
// Validates all the information
},
displayThankYou(){
console.log("Thanks!")
// I'm assuming this is where I should update "mode" to render the component
},
};
I work with forms through redux-form wuth react.js. I need some help with dependent dropdown fields in Dynamic form. I have react component form:
import React, { Component, PropTypes } from 'react'
import { reduxForm, addArrayValue } from 'redux-form'
import PureInput from '../forms/PureInput'
export const fields = [
'children[].type',
'children[].description',
'children[].value',
]
export const contactTypes = [{key:1,val:'E-mail'},{key: 2, val:"Phone"}, {key:3, val:"Social"}]
export const services = [{key:1, val:"VK"},{key:2, val:"Viber"}, {key: 3, val:"Telegram"}]
class ContactForm extends Component {
render() {
const {
addValue,
fields: {
children
},
handleSubmit,
invalid,
submitting
} = this.props
return (
<div className="container">
<form onSubmit={handleSubmit} >
<h3>Основная информация</h3>
<div className="form-group">
{!children.length && <div>No Children</div>}
{children.map((child, index) => (
<div key={index}>
<div>
<label>Child #{index + 1}</label>
<div>
<select className="form-control" field={child.type} onChange={()=> _log('changed')} >
{contactTypes.map(contactType => <option value={contactType.key} key={contactType.key}>{contactType.val}</option>)}
</select>
</div>
<div>
<PureInput type="text" placeholder="type" field={child.description}/>
</div>
<div>
<PureInput type="text" placeholder="description" field={child.value}/>
</div>
<div>
<button type="button" onClick={() => {
children.removeField(index) // remove from index
}}><i/> Remove
</button>
</div>
</div>
</div>
))}
</div>
<button type="button" onClick={() => children.addField()}><i/> addField</button>
</form>
</div>
)
}
}
ContactForm.propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired
}
export default reduxForm({
form: 'ContactForm',
fields
}, undefined, {
addValue: addArrayValue // mapDispatchToProps (will bind action creator to dispatch)
})(ContactForm)
Example:
If the value of the select is child.type == contactTypes[2].key then I need to render the select with
services as options for it, but not like default PureInput with child.description.
How can I do that?