This is Presentation file:
import React, { Component } from 'react'
class CreateZone extends Component {
constructor(){
super()
this.state = {
zone: {
name: '',
zipCodes: []
}
}
}
newZone(event){
let newZone = Object.assign({}, this.state.zone)
newZone[event.target.id] = event.target.value
this.setState({
zone: newZone
})
}
submitZone(event){
this.props.onCreate(this.state.zone)
}
render(){
return(
<div>
<input id="name" onChange={this.newZone.bind(this)} className="form-control" type="text" placeholder="Enter Zone Name" /><br />
<input id="zipCodes" onChange={this.newZone.bind(this)} className="form-control" type="text" placeholder="Enter Zip Code" /><br />
<button onClick={this.submitZone.bind(this)} className="btn btn-danger">Submit Comment</button>
</div>
)
}
}
export default CreateZone
This is Container File:
import React, { Component } from 'react'
import { Zone, CreateZone } from '../presentation'
import { APImanager } from '../../utils'
class Zones extends Component {
constructor(){
super()
this.state = {
zone: {
name: '',
zipCodes: '',
numComments: ''
},
list: []
}
}
componentDidMount(){
console.log('componentDidMount')
APImanager.get('/api/zone', null, (err, response) => {
if(err){
alert('Error in zones: '+err.message)
return
}
this.setState({
list: response.results
})
})
}
submitZone(zone){
let newZone = Object.assign({}, zone)
APImanager.post('/api/zone', newZone, (err, response) => {
if(err) {
alert('ERROR in New Zone: '+err.message)
return
}
console.log('NewZone: '+JSON.stringify(response))
let updatedList = Object.assign([], this.state.list)
updatedList.push(response.result)
this.setState({
list: updatedList
})
})
}
render(){
const listItems = this.state.list.map((zone, i) => {
return (
<li key={i}>
<Zone currentZone={zone} />
</li>
)
})
return(
<div>
<ol>
{listItems}
</ol>
<div>
<CreateZone onCreate={this.submitZone} />
</div>
</div>
)
}
}
export default Zones
React doesn't re-render and console log error is "Cannot read property 'list' of undefined"
It worked fine before I moved form in to presentation file. This is training for me and I would love to understand what is going on
The function submitZone in Zones Component is didn't get the "this" binded. So it was giving "this.state" as undefined. Bind the "this" same as
<button onClick={this.submitZone.bind(this)} className="btn btn-danger">Submit Comment</button>
So replace the line
<CreateZone onCreate={this.submitZone} />
with
<CreateZone onCreate={this.submitZone.bind(this)} />
Related
In this code I am unable to clear both the text fields even though I'm setting their states to blank after clicking on filter via this.Search function.
import React from 'react' import Axios from './Axios' import
'./index.css' export default class Practise extends React.Component {
constructor(props) {
super(props)
this.state = {
lists: [],
names: '',
emails: '',
arr: [],
}
this.Search = (names, emails) => {
const arr = this.state.lists.filter(item => {
if (item.name.includes(this.state.names) && item.email.includes(this.state.emails)) {
return true
} else {
return false
}
})
this.setState({names:''})
this.setState({emails:''})
console.log(arr)
}
}
componentDidMount() {
Axios.get('/comments').then(response => {
// console.log(response.data, 'data')
this.setState({ lists: response.data })
}).catch(response => {
console.log(response, 'Errored!!!')
this.setState({ errored: 'Error has Occured' })
})
}
render() {
const { lists, arr, names, emails } = this.state
return (
<>
<div >
<input type='text' onChange={(e) => this.setState({ names: e.target.value })} />
<input type='text' onChange={(p) => this.setState({ emails: p.target.value })} />
<button onClick={()=>this.Search()}>Filter</button>
</div>
<div>
{lists.length ? lists.map(item =>
<div className='divone' key={item.id}>
Id:- {item.id} <br />
Name:- {item.name} <br />
Email:- {item.email}</div>) : null}
</div>
</>
)
}
};
You have to add the value prop to both of the inputs.After that,
if you change the state to a blank string, the value of inputs will also get blank.Here is an example -
<input type='text' value={this.state.names} onChange={(e) => this.setState({ names: e.target.value })} />
<input type='text' value={this.state.emails} onChange={(p) => this.setState({ emails: p.target.value })} />
It should work.
I'm building a web application to manage some inputs from the user where I want to execute a function on every object in list that is rendered in react. The rendered objects are a different class than the one it is executed in.
import React, { Component } from "react";
import UserInput from "./UserInput";
class Layout extends Component {
objList = []
state = {
update: ""
}
anotherOne = async () => {
this.objList.push(<UserInput key={this.objList.length} />);
this.setState({update: ""});
}
submitCase = async () => {
for (var testCase in this.objList){
this.objList[0].submitInfo();
}
}
removeLatest = async () => {
this.list.pop();
this.setState({update: ""});
}
render(){
return(
<div id="container">
<div>
{ this.objList }
</div>
<div>
<button onClick={ this.anotherOne }>Another One</button>
<button onClick={ this.submitCase }>Submit</button>
</div>
</div>
);
}
}
export default Layout;
import React, { Component } from "react";
class UserInput extends Component {
state = {
name: "",
hairColor: "",
age: ""
}
submitInfo = async () => {
let path = '/dbmanager';
let apiName = "myApi"
let myInit = {
body: {categoryId: "Person", type: this.state.hairColor, data: JSON.stringify(this.state)},
contentType: 'application/json',
}
await API.post(apiName, path, myInit)
.then(response => {
// Add your code here
console.log(response);
})
.catch(error => {
console.log(error.response);
})
}
handleStateUpdate = (event) => {
var eName = event.target.name;
var eValue = event.target.value;
this.setState({[eName]: eValue});
console.log(event.target.value, event.target.name);
}
render(){
return(
<div id="container">
<div>
<label>Name: </label>
</div>
<textarea
type="text"
id="name"
name="name"
value={this.state.name}
onChange={this.handleStateUpdate}/>
<div>
<label>Hair Color: </label>
</div>
<textarea
type="text"
id="hairColor"
name="hairColor"
value={this.state.hairColor}
onChange={this.handleStateUpdate}/>
<div>
<label>Age: </label>
</div>
<textarea
type="text"
id="age"
name="age"
value={this.state.age}
onChange={this.handleStateUpdate}/>
</div>
);
}
}
export default UserInput;
I want the access to the class functions of UserInput so that I could submit the data from all of them on the same button press. Instead the objects are considered functions and are not executable in any means.
I have two hidden input fields that are being populated with the values from a prop when the node-fetch finishes and the values are being set without any issue, but I see the following warning.
Warning: A component is changing a controlled input of type hidden to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
While I understand the differences between controlled and uncontrolled, I can't seem to understand why my component would be creating a conflict between the two. Is there anything clear in my component code that could be causing this?
import React from 'react';
import isEqual from 'lodash/isEqual';
export default class DateFilter extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateValue: '',
endDateValue: ''
};
}
componentDidMount() {
this.setState({
startDateValue: this.props.startDateQuery,
endDateValue: this.props.endDateQuery
});
}
handleChange(input, value) {
this.setState({
[input]: value
})
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
render() {
return (
<div className="col-md-3">
<input type="hidden" name="_csrf" value={this.props.csrf} />
<div className="input-group blog-filter-date-range-picker">
<p>Blog Date Range:</p>
</div>
<div className="input-group blogFilterDatePicker">
<span className="input-group-addon"><i className="glyphicon glyphicon-calendar"></i></span>
<input type="text" name="blogDateRange" className="form-control blogFilterDatePicker" autoComplete="off" />
</div>
<input type="hidden" name="blogStartDate" className="form-control" value={this.state.startDateValue} onChange={e => this.handleChange('startDateValue', e.target.value)} />
<input type="hidden" name="blogEndDate" className="form-control" value={this.state.endDateValue} onChange={e => this.handleChange('endDateValue', e.target.value)} />
</div>
);
}
}
Parent Component:
import React from 'react';
import CatgoryFilter from './SearchFormFilters/CategoryFilter';
import DepartmentFilter from './SearchFormFilters/DepartmentFilter';
import TeamFilter from './SearchFormFilters/TeamFilter';
import TypeFilter from './SearchFormFilters/TypeFilter';
import DateFilter from './SearchFormFilters/DateFilter';
//Activity Feed - Search Form
export default class ActivityFeedSearchForm extends React.Component {
render() {
var clearFilters;
if(this.typeQuery || this.props.categoryQuery || this.props.departmentQuery || this.props.teamQuery || this.props.startDateQuery || this.props.endDateQuery || this.props.typeQuery){
clearFilters = Clear;
}
return (
<div className="row">
<div className="annotation-search-form col-md-10 col-md-offset-1">
<div clas="row">
<form action="/app" method="post" className="annotation-filter-fields">
<DateFilter csrf={this.props.csrf} startDateQuery={this.props.startDateQuery} endDateQuery={this.props.endDateQuery} />
<TypeFilter typeQuery={this.props.typeQuery} />
<CatgoryFilter category={this.props.category} categoryQuery={this.props.categoryQuery} />
<DepartmentFilter department={this.props.department} departmentQuery={this.props.departmentQuery} />
<TeamFilter team={this.props.team} teamQuery={this.props.teamQuery} />
<div className="col-md-1 annotation-filter-section filter-button-container">
<button type="submit" id="annotation-filter-submit">Filter</button>
{clearFilters}
</div>
</form>
</div>
</div>
</div>
)
}
}
Top-level Parent Component:
import React from 'react';
import fetch from 'node-fetch';
import ReactMarkdown from 'react-markdown';
import path from 'path';
import ActivityFeedSearchForm from './ActivityFeedSearchForm/ActivityFeedSearchForm';
import { API_ROOT } from '../config/api-config';
//GET /api/test and set to state
export default class ActivityFeed extends React.Component{
constructor(props, context) {
super(props, context);
this.state = this.context.data || window.__INITIAL_STATE__ || { team: [] };
}
fetchList() {
fetch(`${API_ROOT}` + '/api' + window.location.search, { compress: false })
.then(res => {
return res.json();
})
.then(data => {
this.setState({
startDateQuery: data.startDateQuery,
endDateQuery: data.endDateQuery,
});
})
.catch(err => {
console.log(err);
});
}
componentDidMount() {
this.fetchList();
}
render() {
return (
<div>
<Navigation notifications={this.state.notifications}/>
<ActivityFeedSearchForm csrf={this.state.csrf} category={this.state.category} categoryQuery={this.state.categoryQuery} department={this.state.department} departmentQuery={this.state.departmentQuery} team={this.state.team} teamQuery={this.state.teamQuery} typeQuery={this.state.typeQuery} startDateQuery={this.state.startDateQuery} endDateQuery={this.state.endDateQuery} />
<div className="activity-feed-container">
<div className="container">
<OnboardingInformation onboarding={this.state.onboardingWelcome} />
<LoadingIndicator loading={this.state.isLoading} />
<ActivityFeedLayout {...this.state} />
</div>
</div>
</div>
)
}
};
As you are dealing with only startDateValue and endDateValue in componentWillReceiveProps it’s good to compare them individually instead of whole props. Deep equality check is not happening for whole props and that’s why you get the warning
Change
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
To
componentWillReceiveProps(nextProps) {
if (this.props.startDateQuery != nextProps.startDateQuery && this.props.endDateQuery != nextProps.endDateQuery){
this.setState({ startDateValue: nextProps.startDateQuery, endDateValue: nextProps.endDateQuery });
}
}
Also you don’t need componentDidMount so remove that part in your code and update constructor code with below one
constructor(props) {
super(props);
this.state = {
startDateValue: this.props.startDateQuery ? this.props.startDateQuery: '',
endDateValue:this.props.endDateQuery ? this.props.endDateQuery: ''
};
}
}
And
Change
<input type="hidden" name="_csrf" value={this.props.csrf} />
To
<input type="hidden" name="_csrf" value={this.props.csrf ? this.props.csrf : "" />
And you are not binding handleChange so either bind it manually in constructor or change it to arrow function like below
Change
handleChange(input, value) {
this.setState({
[input]: value
})
}
To
handleChange = (input, value) => {
this.setState({
[input]: value
})
}
I'm learning ReactJS and I'm creating a editable Pokémon list based on this guide.
When I try to pass a function to edit a list item (at this point I want to click the item and get the name), I get TypeError: Cannot read property 'edit' of undefined on the following line of the addPokemon function: onClick={() => this.edit(pokemon.name)}
Code:
PkmnForm
import React, { Component } from "react";
import PkmnList from "./PkmnList";
class PkmnForm extends Component {
static types = [
'Bug',
'Dragon',
'Ice',
'Fighting',
'Fire',
'Flying',
'Grass',
'Ghost',
'Ground',
'Electric',
'Normal',
'Poison',
'Psychic',
'Rock',
'Water'
]
constructor(props) {
super(props);
this.state = {
name: '',
type: '',
pokemons: [],
caught: false,
};
}
handleSubmit = (event) => {
var pokemon = {
name: this.state.name,
type: this.state.type,
caught: this.state.caught
};
this.setState({
name: '',
type: '',
caught: false,
pokemons: this.state.pokemons.concat(pokemon)
});
event.preventDefault()
}
handleChange = (event) => {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleTypeChange = (event) => {
this.setState({
type: event.target.value,
})
}
editPokemon(name) {
console.log(name);
}
render() {
return (
<div>
<div>
<h1>Register a Pokémon</h1>
<form onSubmit={this.handleSubmit}>
<input
required
placeholder=" Name"
name="name"
onChange={this.handleChange}
value={this.state.name}
/>
<br />
<select
name="type"
required
value={this.state.type}
onChange={this.handleChange}
>
<option value='' disabled>Type</option>
{PkmnForm.types.map(
optionValue => (
<option
key={optionValue}
value={optionValue}
>
{optionValue}
</option>
)
)}
</select>
<br />
<label>
Caught
<input
name="caught"
type="checkbox"
checked={this.state.caught}
onChange={this.handleChange}
/>
</label>
<br />
<button type="submit">Add Pokemon</button>
</form>
</div>
<div>
<PkmnList
pokemons={this.state.pokemons}
edit={this.editPokemon}
/>
</div>
</div>
);
}
}
export default PkmnForm;
PkmnList
import React, { Component } from 'react';
class PkmnList extends Component {
constructor(props) {
super(props);
this.edit = this.edit.bind(this);
}
edit(name) {
this.props.edit(name);
}
addPokemon(pokemon) {
return <li
onClick={() => this.edit(pokemon.name)}
key={pokemon.name}
>
{pokemon.name} – {pokemon.type} {pokemon.caught ? '(caught)' : ''}
</li>
}
render() {
var pokemons = this.props.pokemons;
var listItems = pokemons.map(this.addPokemon);
return (
<ul>
{listItems}
</ul>
);
}
}
export default PkmnList;
Thanks :-)
The problem is in this line:
var listItems = pokemons.map(this.addPokemon);
Here, you are passing this.addPokemon as the function to map. But that function is not bound to this, so inside of it, this is not available.
You either have to bind it explicitly by calling .bind(this), just like you did with the edit function:
var listItems = pokemons.map(this.addPokemon.bind(this));
Or you can pass an arrow function that calls the method:
var listItems = pokemons.map(x => this.addPokemon(x));
You need to bind editPokemon like so:
constructor(props) {
super(props);
this.editPokemon = this.editPokemon.bind(this);
}
Or you can also use arrow functions, which have proper scoping:
editPokemon = (pokemon) => {
...
}
I have a Component that is handling a contact forum submission from a user. I want to take the state that the user submits and add it to my props data. Right now everything is working, but the handleSubmit, I am not sure how to take the state and pass it to my this.data.props to update the data to include the new object.
My data is an array of Objects. The state takes user input and updates itself. Next I want to take the state object and add it to my props.data and then display it on the screen.
EDIT: UPDATED WITH LATEST CODE
import React, { Component, PropTypes } from 'react';
const testData = [
{
name: 'Joe',
email: 'joemail'
},
{
name: 'Bill',
email: 'billmail'
},
{
name: 'Dude',
email: 'dudemail'
}
]
class FormContact extends Component {
constructor(props) {
super(props)
this.state = {
formValues: {
name: '',
email: ''
}
}
}
handleChange(event) {
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("NEW FORM VALUES " + this.state.formValues.name + " " + this.state.formValues.email);
const {name, email} = this.state.formValues
this.props.addContact({name, email});
}
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>
)
}
}
FormContact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
FormContact.defaultProps = {
data: testData
}
class Contact extends Component {
constructor(props) {
super(props)
this.state = {
data: testData
}
}
addContact(contact) {
this.setState({data: this.state.data.concat(contact)});
}
render() {
const renObjData = this.props.data.map( (anObjectMapped, index) => {
return (<p key={index}>
Name: {anObjectMapped.name} < br/>
Email: {anObjectMapped.email} <br /></p>
)
});
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact.bind(this)} />
{renObjData}
</div>
)
}
}
Contact.PropTypes = {
data: PropTypes.arrayOf(
PropTypes.object
)
}
Contact.defaultProps = {
data: testData
}
export default Contact;
What you are looking at here is having a parent container that passes down data as props to the form component. You already have your Contact component so you can make it hold the data state.
How it would work is you would write a function on the Contact component called addContact and it would take a contact as an argument and then set its own state with the new contact IE concat it to its own data array through setting state.
class Contact extends React.Component {
constructor() {
super();
this.state = {
data: testData
}
}
addContact = (contact) => {
this.setState({data: this.state.data.concat(contact)});
};
render() {
const contacts = _.map(this.state.data, (value, index) => {
return <li key={index + value}> {value.email} {value.name} </li>
})
return (
<div>
<h1>CONTACT PAGE</h1>
<FormContact data={this.state.data} addContact={this.addContact} />
<h3> Contacts</h3>
<ul>{contacts} </ul>
</div>
)
}
}
and then in your handleSubmit function all you have to do is add
handleSubmit(event) {
event.preventDefault();
const {name, email} = this.state.formValues
this.props.addContact({name, email})
}
this will push it onto the data array in the parent component and then once the parent component updates it will pass that down as props to the form component.
Here is a code pen showing all that in action. http://codepen.io/finalfreq/pen/VKPXoN
UPDATE: Also in Contacts added how to display data, you can easily replace lodash _.map with this.state.data.map(function(value, index)