React couldn't read the state - reactjs

I used the same function I got on a react native in a react app and it didn't work, looks like I couldn't access the sate although I defined it in the constructor, the goal is to push data to firebase, I tried with random strings and it definitely works, it's just when using the form that it crashes.
As you can see I'm using text components to take a look a the state on the HTML page :
import React, { Component } from 'react';
import fire from './config/Fire';
class Home extends Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
isOpen: false,
title: '',
description: '',
loading: true
};
}
handleChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
saveData(e) {
e.preventDefault();
let title = this.state.title;
let description = this.state.description;
const { currentUser } = fire.auth();
fire
.database()
.ref(`/master/setup/`)
.push({ title, description })
.then(() => {
this.setState({ loading: false }).catch(error => {
console.log(error);
});
});
}
render() {
return (
<div>
<Container>
<Row>
<Col sm="2" lg="3" />
<Col sm="8" lg="6">
<h1>General Setup</h1>
<form>
<div class="form-group">
<label for="exampleInputEmail1">Title</label>
<input
value={this.state.title}
onChange={this.handleChange}
name="title"
class="form-control"
id="title"
placeholder="Enter event title"
/>
</div>
<div class="form-group">
<label for="exampleInputEmail1">Description</label>
<input
value={this.state.description}
onChange={this.handleChange}
name="description"
class="form-control"
id="description"
placeholder="Enter event description"
/>
</div>
<button onClick onClick={this.saveData} class="btn btn-primary">
Submit
</button>
</form>
<p>{this.state.title}</p>
<p>{this.state.description}</p>
<p>{this.state.loading.toString()}</p>
</Col>
<Col sm="2" lg="3" />
</Row>
</Container>
</div>
);
}
}
export default Home;
TypeError: Cannot read property 'state' of undefined
Please, someone, let me know what's going on with this code?

You can change saveData to an arrow function hence binding isn't required. This is an ES6 version, do something like below
saveData = e => {
e.preventDefault();
let title = this.state.title;
let description = this.state.description;
const { currentUser } = fire.auth();
fire.database().ref(`/master/setup/`)
.push({ title, description })
.then(() => {
this.setState({ loading: false})
.catch((error) => {
console.log(error);
})
});
}

You need to bind saveData in constructor.
this.saveData = this.saveData.bind(this);

You forgot to bind scope to saveData method.
Do it in constructor same as you bind it to handleChange method.
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.saveData = this.saveData.bind(this);
this.state = {
isOpen: false,
title: '',
description: '',
loading: true,
};
or
change saveData definition to one that uses arrow function syntax from ES6
saveData = (e) => {...function body as you already have it}
and parent scope will be bind for you by default

Related

Passing submitted form data to another component

I have the following code, it basically accepts some basic input and when submit button is clicked user is notified with an alert, state is constantly being updated via onChange event. What i wonder is can i somehow pass the retrieved data to another component inside the event handler for submit button (which i have called handleFormSubmit)? I have recently seen react has something called 'context' ...maybe that would be best here? Advice please? :)
class Form extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
comments: '',
topic: 'react'
}
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handleCommentsChange = this.handleCommentsChange.bind(this);
this.handleTopicChange = this.handleTopicChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleUsernameChange(event){
this.setState({
username: event.target.value
},
() =>{
console.log(this.state.username)
})
}
handleCommentsChange(event){
this.setState({
comments: event.target.value
},
() =>{
console.log(this.state.comments)
})
}
handleTopicChange(event){
this.setState({
topic: event.target.value
},
() =>{
console.log(this.state.topic)
})
}
handleFormSubmit(event){
event.preventDefault();
alert(`${this.state.username} ${this.state.comments} ${this.state.topic}`);
}
render() {
return (
<form onSubmit={this.handleFormSubmit}>
<div>
<label>Username</label>
<input type='text' value={this.state.username} onChange={this.handleUsernameChange}/>
</div>
<div>
<textarea value={this.state.comments} onChange={this.handleCommentsChange}></textarea>
</div>
<div>
<select value={this.state.topic} onChange={this.handleTopicChange}>
<option value="react">React</option>
<option value="angular">Angular</option>
<option value="vue">Vue</option>
</select>
</div>
<button>Submit</button>
</form>
)
}
}
Hi all i made some changes and got something working, added extra state attribute called dataSubmitted set it to false then only after i submit the data is child (which i called AcceptFormData) allowed to render and i pass the state attributes as props. I do not know if this is a good approach or not but it works and no console errors.
class Form extends Component {
constructor(props) {
super(props)
this.state = {
username: '',
comments: '',
topic: 'react',
dataSubmitted: false
}
this.handleUsernameChange = this.handleUsernameChange.bind(this);
this.handleCommentsChange = this.handleCommentsChange.bind(this);
this.handleTopicChange = this.handleTopicChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleUsernameChange(event){
this.setState({
username: event.target.value
},
() =>{
console.log(this.state.username)
})
}
handleCommentsChange(event){
this.setState({
comments: event.target.value
},
() =>{
console.log(this.state.comments)
})
}
handleTopicChange(event){
this.setState({
topic: event.target.value
},
() =>{
console.log(this.state.topic)
})
}
handleFormSubmit(event){
event.preventDefault();
this.setState({
dataSubmitted: true
})
}
render() {
if(this.state.dataSubmitted === false){
return (
<form onSubmit={this.handleFormSubmit}>
<div>
<label>Username</label>
<input type='text' value={this.state.username} onChange={this.handleUsernameChange}/>
</div>
<div>
<textarea value={this.state.comments} onChange={this.handleCommentsChange}></textarea>
</div>
<div>
<select value={this.state.topic} onChange={this.handleTopicChange}>
<option value="react">React</option>
<option value="angular">Angular</option>
<option value="vue">Vue</option>
</select>
</div>
<button>Submit</button>
</form>
)
}else{
return (
<AcceptFormData username={this.state.username} comments={this.state.comments} topic={this.state.topic}/>
)
}
}
}
export default Form

pass object to setState fails

I created my own web-app of posts where you able to add,delete and update posts. Right now when I try to add a post and pass the inputs value throw a function and then execute 'setState' I get an error, something is wrong in the way I do it. could you help out?
I could just remove the root object from the state ('post') and just use the title,body as for themselfs. But I want to structure it this way: Post:{title,body}
import React, { Component } from 'react';
export class addPost extends Component {
state = {
post: {
title: '',
body: ''
}
};
handleChange = e => {
this.setState({ post[e.currentTarget.id]: e.currentTarget.value });
console.log(this.state);
};
handleSubmit = () => {};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
Enter title:
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
id="title"
/>
Enter body:
<input
type="text"
value={this.state.body}
onChange={this.handleChange}
id="body"
/>
</form>
</div>
);
}
}
export default addPost;
I expect to pass this object post:{title:'sometext',body:'sometext'}
You need to fix on two places
Where you are setting state
Where you are accessing state in input
handleChange = e => {
this.setState(prevState => ({
post: { ...prevState.post, [e.target.id]: e.target.value }
}));
};
<input value={this.state.post.title />
<input value={this.state.post.body} />
Made a sandbox for you: https://codesandbox.io/s/p2w7765j0
Most conventions are to use the name property for your inputs and map them using event.target.name
import React, { Component } from "react";
class AddPost extends Component {
state = {
post: {
title: "",
body: ""
}
};
handleChange = event => {
this.setState(
{
post: {
...this.state.post,
[event.target.name]: event.target.value
}
},
() => console.log(this.state)
);
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
Enter title:
<input
type="text"
value={this.state.post.title}
onChange={this.handleChange}
name="title"
/>
Enter body:
<input
type="text"
value={this.state.post.body}
onChange={this.handleChange}
name="body"
/>
</form>
</div>
);
}
}
export default AddPost;

How to push to an object to an array which is a state object, react way with Typescript

I need to maintain array of objects which is stored in a state object. Basically I need to push each object to this array whenever I click on Add button .This should basically store this object in array.
Also I am unable to fetch proper values when I am trying to submit?
Where am I going wrong?
Basically the structure I want is:
users= [
{"name":"xxx","email":"yyy","phone":"656"},
{"name":"yyy","email":"xxx","phone":"55"}
];
import * as React from 'react';
interface IState{
users : Account[];
}
interface Account{
name: string;
email: string;
phone: string
}
export default class App extends React.Component<{},IState> {
constructor(props:any){
super(props);
this.state= {
users: []
}
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
users:{
...this.state.users,
[event.target.name]:event.target.value
}
})
}
onAdd = () => {
this.setState((prevState) => ({
users: [...prevState.users],
}));
console.log(this.state.users); // Unable to get the proper info
}
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
Update onAdd like below, because setState method updates state asynchronously, therefore you can't get the state right after calling it, but you can do so using a callback as second argument in setState method which is invoked after the method has updated the state
onAdd = () => {
this.setState((prevState) => {
const newUser = {}
return {
users: [...prevState.users, newUser],
}
}, () => {
console.log(this.state.users)
});
}
Things to correct:-
1) You are using only one state variable users for one user as well as all users. So create two state variables, one for temporary storing of data for a user and users variable for storing all users data.
2) You are trying to access console.log(this.state.users); after setState but it is not in the callback, setState is asynchronous it should be in callback of setState.
3) When user submits the form, the page refreshes which is default behaviour of application, we need e.preventDefault(); to override this behaviour.
4) Use state for individual input textboxes so that you could may be apply validation etc on fields.
import * as React from "react";
import { render } from "react-dom";
interface IState {
users : Account[],
user: Account
}
interface Account{
name: string;
email: string;
phone: string
}
class App extends React.Component<{}, IState> {
constructor(props: any) {
super(props);
this.state = {
users: [],
user: {name: '', email:'', phone: ''}
}
}
handleChange = (event: React.FormEvent<HTMLInputElement>) => {
this.setState({
user: {
...this.state.user,
[event.currentTarget.name]: event.currentTarget.value
}
});
};
onAdd = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
this.setState(
{
users: [...this.state.users, this.state.user],
user: {name:'', email: '', phone: ''}
},
() => {
console.log("updated state", this.state.users);
}
);
};
public render() {
const { name, email, phone } = this.state.user;
return (
<form onSubmit={this.onAdd}>
<input type="text" onChange={this.handleChange} value={name} name="name" />
<input type="text" onChange={this.handleChange} value={email} name="email" />
<input type="text" onChange={this.handleChange} value={phone} name="phone" />
<button type="submit">Add</button>
</form>
);
}
}
render(<App />, document.getElementById("root"));
Improvement Area - You could declare array of fields like fields = ['name', 'phone', 'email'] and map over in render function, this way you would need to write form once and any no of fields could be added.
Hope that helps!!!
you don't need handleChange, in onAdd get all inputs value from the event, put them in object( like {name: event.target.form.elements.name.value ...} and set it in users(which will be an array)
export default class App extends React.Component{
constructor(props:any){
super(props);
this.state = {
users: []
}
}
onAdd = (event) => {
const user = {
name: event.target.form.elements.name.value,
email: event.target.form.elements.email.value,
phone: event.target.form.elements.phone.value
}
this.setState({
users: [...this.state.users, user]
});
}
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" name="name" />
<input type="text" name="email" />
<input type="text" name="phone" />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
then if you log this.state.users you will get the stractue you need
users= [
{"name":"xxx","email":"yyy","phone":"656"},
{"name":"yyy","email":"xxx","phone":"55"}
];
export default class App extends React.Component<{},IState> {
state = {
users: [],
text: '',
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
[e.target.name]: e.target.value
})
}
onAdd = () => this.setState({
users:[
...this.state.users,
{
e.target.name: e.target.value,
e.target.email: e.target.value,
e.target.phone: e.target.value
}
]
})
render(){
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"name"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"email"} />
<input type="text" onChange={(e:any) => this.handleChange(e)} name={"phone"} />
<button type="submit">Add</button>
</form>
</React.Fragment>
}
}
You are setting your users as an array and then you are converting it into an object with the handleChange. Try to see if this works

Adding two input values in one state Reactjs

I want to add a field of trip_start and trip_end in one state called name so that when I send the data, it's combined into one field name.
This is what happens when I submit the data, when I click the first time the state for name shows null, when I click for the second time that's when the state of name is populated with the data of trip_start and trip_end.
Below is my code :
export class DriverPage extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
trip_start:'',
trip_end:'',
nameError:'',
details:'',
detailsError:'',
price: '',
priceError:'',
driver_name: localStorage.getItem('username')
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit = (event) => {
event.preventDefault();
console.log('state ', this.state.name)
this.setState({name:''+this.state.trip_start+' to ' + this.state.trip_end});
};
handleChange =(evt) => {
this.setState({ [evt.target.name]: evt.target.value });
}
Below is my component which contains the HTML code :
export const DriverComponent = ({handleSubmit, handleChange,obj})=>(
<div className={'bg-image'}>
<Card className={'landing-card'}>
<form onSubmit={handleSubmit}>
<Row>
<Col s={12} m={12} l={12}>
<label s={12} l={12} className={'header'}> Make Ride Request </label>
</Col>
<Input s={12} l={12} type={'text'} value={obj.trip_start} label="Trip Start" name='trip_start' onChange={handleChange}/>
<Input s={12} l={12} type={'text'} value={obj.trip_end} label="Trip End" name='trip_end' onChange={handleChange}/>
<Col s={12} m={12} l={6}>
<div className={'errors'} s={10}>{obj.nameError}</div>
</Col>
<div className="input-field col s12">
<textarea id="details" name='details' onChange={handleChange} className="materialize-textarea"></textarea>
<label htmlFor="details">Trip Details</label>
</div>
<Col s={12} m={12} l={6}>
<div className={'errors'}>{obj.detailsError}</div>
</Col>
<Input s={12} l={12} type="text" value={obj.price} label="Price" name='price' onChange={handleChange} />
<Col s={12} m={12} l={6}>
<div className={'errors'}>{obj.priceError}</div>
</Col>
<Col s={12} m={12} l={12}>
<Button waves='light' className={`purple button-align`} value='submit' type='submit' >Create Ride Request</Button>
</Col>
</Row>
</form>
</Card>
</div>
);
So in summary, I want the state trip_start and trip_end to be concatenated on the state name instantly while am going to send the data.
For example this is a simple pseudo code for what I want to achieve:
trip_start = new york
trip_end = toronto
name = trip_start + "to " +trip_end
Output
name = new york to toronto
Is there any reason why it has to be concatenated before submission? Seems like you're making your life harder by combining two asynchronous strings synchronously. Instead, just make your life easier and concatenate upon form submission.
Concatenated on form submission example (best solution): https://codesandbox.io/s/10w18m5z54
containers/Form.js
import React, { Component } from "react";
import Fields from "../components/Fields";
export default class Form extends Component {
state = {
tripStart: "",
tripEnd: ""
};
handleChange = ({ target: { value, name } }) =>
this.setState({
[name]: value
});
handleSubmit = e => {
e.preventDefault();
const { tripEnd, tripStart } = this.state;
const name = `${tripStart} to ${tripEnd}`;
alert(name);
};
render = () => (
<Fields
{...this.state}
handleSubmit={this.handleSubmit}
onHandleChange={this.handleChange}
/>
);
}
Concatenated on input change example (works, but not an ideal solution as it causes double rerendering -- also, this.state.name is never used in your component, so utilizing state is unnecessary and not recommended!): https://codesandbox.io/s/nk66v4rk9j
containers/Form.js
import React, { Component } from "react";
import Fields from "../components/Fields";
export default class Form extends Component {
state = {
tripStart: "",
tripEnd: ""
};
handleChange = ({ target: { value, name } }) =>
this.setState(
{
[name]: value
},
() =>
this.setState({
name: `${this.state.tripStart} to ${this.state.tripEnd}`
})
);
handleSubmit = e => {
e.preventDefault();
const { name } = this.state;
alert(name);
};
render = () => (
<Fields
{...this.state}
handleSubmit={this.handleSubmit}
onHandleChange={this.handleChange}
/>
);
}

ReactJS - pass input values from child to parent

child component
import React, { Component } from 'react'
export default class Login extends Component {
constructor (props) {
super(props);
this.state = {Id: '',name: '',gender: ''};
this.show = this.show.bind(this);
}
show (event) {
if (this.state.Id === "123456" && this.state.name !== '' && this.state.gender !== '') {
this.props.show();
alert('you are login');
console.log('A ID was submitted: ' + this.state.Id);
console.log('A Name was submitted: ' + this.state.name);
console.log('A Gender was submitted: ' + this.state.gender);
} else {
alert('Please enter your valid id,Your Name & Gender');
}
event.preventDefault();
}
render () {
return (
<div className="login">
<form onSubmit={ this.show.bind(this) }>
<div>
<label>Your ID:</label>
<input type="text" onChange={ event => this.setState({ Id: event.target.value }) } placeholder="Enter your ID" />
</div>
<br />
<div>
<label>Your Name:</label>
<input type="text" onChange={ event => this.setState({ name: event.target.value }) } placeholder="Enter your Name" />
</div>
<br />
<div>
<label>Your Gender:</label>
<label>Female:</label>
<input type="radio" name="gender" value="Female" onChange=
{ event => this.setState({ gender: event.target.value }) } />
<label>Male:</label>
<input type="radio" name="gender" value="Female" onChange={ event => this.setState({ gender: event.target.value }) } />
</div>
<input type="submit" value="Submit" onClick={ this.props.comingvalue } />
</form>
</div>
)
}
}
parent component
class App extends Component {
constructor (props) {
super(props);
this.state = { Id: '', name: '', gender: '' };
}
getvalue () {
console.log('getting values as props');
this.setState({ Id: this.state.Id });
this.setState({ name: this.state.name });
this.setState({ gender: this.state.gender });
}
render () {
return (
<div className="App">
<Login comingvalue={ this.getvalue } />
<button type="button" className="btn btn-primary" onClick=
{ this.handleLogin }>Sign In</button>
</div>
);
}
}
export default App;
now here is the my question i want that when i enter value in my child component i get those values in parent compnent how i can get this please help..'i thing you peeple should know that i cut alot of code from above code there is possibilty of any other error but i want to know only one thing which i mention above i want child coponents value in parent component.. please suggest me right solution..thanks
Just a pointer for future posts: the less code the better and please, for the love of God, make sure the formatting is correct.
A standard pattern in React for passing information back up the tree is to pass children a callback as a prop.
parent
class Parent extends React.Component {
onChildCallback = (data) => {
alert(data)
}
render() {
return (
<div>
...
<Child onAction={this.onChildCallback}/>
</div>
)
}
}
child
class Child extends React.Component {
render() {
return (
<button onClick={() => this.props.onAction('hello from the child')}>
Click Me!
</button>
)
}
}
this is, of course, simplified, but you can extend it however you like. Some things to watch out for:
make sure you're either binding the callback in the parent or using arrow functions (in this case, I'm using a ES7 class property)
if you need data from a child of a child, you need to chain these... you can get away with using context, but ... don't. Just don't.

Resources