How to get state of a child component in react js ? - javascript

I have a Login Component and Input Components :
class Login extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeInput = this.handleChangeInput.bind(this);
this.state = {
email_user: "LOL",
password_user: "",
};
}
handleSubmit(e) {
//alert('A name was submitted: ');
e.preventDefault();
console.log(this.state);
}
handleChangeInput(e){
console.log(e.target.value);
this.setState({value: e.target.value});
}
render() {
return (
<Input size="L" type="email" name="email_user" placeholder="Email" value={this.state.email_user} />
<Input size="L" type="password" name="password_user" placeholder="Mot de passe" value={this.state.password_user} onChange={this.handleChangeInput} /> );
}
}
My Input component returns some HTML like :
<div className={classInput}>
<span className="input__text">
<input type={this.props.type} name={this.props.name} placeholder={this.props.placeholder} defaultValue={this.props.value} onChange={this.handleChange} required />
</span>
</div>
My Input state is updated great by the handleChange inside the Input Component.
But these state are not visible by the Login Component. With or without handleChangeInput, the states in theLogin Component are not saved.
How the Login Component can access to the update state inside child component please?

Related

Implement recursive onClick event in react js

I am trying to implement a recursive method on reactjs, but only when data is submitted or clicked.
Following is the code I am trying with:
import React, { Component } from "react";
class Textbox extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {this.setState({value: event.target.value}); }
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="React" />
</div>
</form>
);
}
}
export default Textbox;
which generates the following view.
I want to use a recursive method onClick or onSubmit, such that it will generate another text box upon submission. Like
which I want to continue until I click some "Exit" or "Stop" button, which I can add to the top of the view.
From what I have read about recursive implementation on ReactJS, I need to call the class/function again inside render. When I do that I think react is getting inside the infinite loop and freezes the browser.
what I tried is to call Textbox inside the <div> <div/> of the render method. Like this:
.... other code lines are same
<div>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="React" />
<Textbox/>
</div>
How can I generate a recursive textbox on submission/clicking event on the previous text box?
You could do it like this, where showNextInput prevents the infinite loop:
class Textbox extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
showNextInput: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {this.setState({value: event.target.value}); }
handleSubmit(event) {
console.log('submit');
event.preventDefault();
this.setState({ showNextInput: true });
}
render() {
return (
<>
<form onSubmit={this.handleSubmit}>
<div>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</div>
</form>
{ this.state.showNextInput ? <Textbox/> : null }
</>
);
}
}
However, your use case looks like something you would usually do by
managing a list of values somewhere,
add items as required inside your handlers, and
then display a list of these items
Here a quick and dirty example:
export class TextboxList extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {
0: ''
},
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(index, value) {
this.setState({
values: {
...this.state.values,
[index]: value
}
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({
values: {
...this.state.values,
[Object.keys(this.state.values).length]: '' // add new empty item to list
}
});
}
render() {
return (
<>
<form onSubmit={this.handleSubmit}>
{ Object.keys(this.state.values).map( (index) => {
const value = this.state.values[index];
return <div key={ index }>
<label>
Name:
<input
type="text"
value={ value }
onChange={ (event) => this.handleChange( index, event.target.value ) }
/>
</label>
<input type="submit" value="Submit" />
</div>;
})}
</form>
</>
);
}
}
export default Textbox;

Why does my React form auto-refresh the page even if I put "event.preventDefault()" on handleSubmit?

I have two files which work together to render things. The first is App.js, which first renders Form.js. The form will then collect information, which on submission, changes the Form state and calls a function from App.js. This function is called "createProject." Calling "createProject" in Form.js "handleSubmit" makes the page auto-refresh. However, if I remove "createProject" from handleSubmit, the page does not auto-refresh. Here are the two files.
import React, { Component } from "react";
import Project from "./components/Project.js"
import Form from "./Form.js";
class App extends Component {
constructor(props) {
super(props);
this.state = {
projectList: [],
myProjects: [],
userList: [],
submitted: false
};
this.createProject = this.createProject.bind(this);
}
createProject(title, desc, langs, len, exp) {
this.setState({
projectList: this.state.projectList.push([
{
title : title,
description : desc,
language : langs,
length : len,
experience : exp
}
]),
submitted : true
});
}
deleteProject(title) {
const projects = this.state.projectList.filter(
p => p.title !== title
);
this.setState({projects});
}
render() {
let info;
if (this.state.submitted) {
info = (
<div>
<p>cccc</p>
</div>
);
} else {
info = (
<br/>
);
}
return(
<div>
<Form/>
{info}
{this.state.projectList.map((params) =>
<Project {...params}/>)}
</div>
);
}
}
export default App;
import React from "react";
import createProject from "./App.js"
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
description: "",
language: "",
length: 0,
experience: "",
submitted: false
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(event) {
this.setState({
submitted: true
})
createProject(
this.state.title,
this.state.description,
this.state.language,
this.state.length,
this.state.experience
)
event.preventDefault();
}
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
let info;
if (this.state.submitted) {
info = (
<div>
<h1>{this.state.title}</h1>
<p>{this.state.description}</p>
<p>{this.state.language}</p>
<p>{this.state.length}</p>
<p>{this.state.experience}</p>
</div>
);
} else {
info = <br/>;
}
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Title:
<input
name="title"
type="textbox"
checked={this.state.title}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Description:
<input
name="description"
type="textbox"
checked={this.state.description}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Language:
<input
name="language"
type="textbox"
checked={this.state.language}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Length:
<input
name="length"
type="number"
checked={this.state.length}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Experience:
<input
name="experience"
type="textbox"
checked={this.state.experience}
onChange={this.handleInputChange} />
</label>
<br />
<input type="submit" value="Submit" />
</form>
{info}
</div>
);
}
}
export default Form;
I've also tried adding "new" to the "createProject" in handleSubmit, and while that does stop the auto-refresh, it will not call the createProject function. (Or maybe it does, but none of the code in the createProject function seems to be run.) Can anyone help with preventing this auto refresh while also allowing App's createProject function to run properly?
The page auto refreshes because execution never gets to your event.PreventDefault() line. This is due to an error encountered when react tries to evaluate createProject. To fix this, correct handleSubmit like so.
handleSubmit(event) {
event.preventDefault(); // moved up in execution.
this.setState({
submitted: true
})
createProject(
this.state.title,
this.state.description,
this.state.language,
this.state.length,
this.state.experience
)
}
Notice that moving event.PreventDefault() to the top of your handleSubmit(event) function just before this.setState line prevents default form behaviour on submit.
You however get an error because App.js doesn't export a function called createProject. To maintain the createProject within App instance, you need to pass it as a prop which you can then reference as this.props.createProject.
See this answer on how to do call a Parent method in ReactJS.

Passing Multiple Refs from Child Component to Parent Component-Reactjs

I have two components, the Edit(child) and the Index(parent).
There are three input boxes in the Edit component and I'll like to:
Pass these three refs to the Index component
Compare these inputs gotten via refs (in the HandleUpdate function specifically)
Edit.js:
<form onSubmit={props.handleUpdate}>
<input
className=""
name="name"
ref={props.setRef}
onChange={props.handleChange}
defaultValue= {props.name} />
<input
className=""
type="number"
name="day"
min="1"
max="31"
ref={props.setRef}
onChange={props.handleChange}
defaultValue= {props.day} />
<input
className=""
name="dob"
type="month"
ref={props.setRef}
onChange={props.handleChange}
defaultValue={props.dob} />
Index.js:
class BirthdaylistKeeper extends React.Component{
constructor(props){
super(props);
//state
}
...
handleUpdate(event){
event.preventDefault();
//if((name.value === "") && (dob.value === "") && (day.value === "")){
// console.log("empty");
//}
const item = this.state.currentItem;
let index = this.state.items.indexOf(item);
const newItemList = [...this.state.items];
newItemList.splice(index, 1, this.state.dataEdited);
this.setState({
items: [...newItemList],
toggle: false
});
}
//...
render(){
return(
...
<Entry
name={this.state.name}
day={this.state.day}
dob={this.state.dob}
onChange={this.handleChange}
onSubmit={this.handleSubmit}
setRef={this.setRef} />
)
}
How can I achieve this?
I have an idea, instead of passing refs from child to parent, make the refs in the parent and pass them to the child component and then assign them to each input element, something like the following codes:
The parent component:
import React, { Component, createRef } from 'react';
class BirthdaylistKeeper extends Component{
constructor(props) {
super(props);
this.nameRef = createRef();
this.dayRef = createRef();
this.dobRef = createRef();
//state
}
~~~
render() {
return(
~~~
<Entry
nameRef={this.nameRef}
dayRef={this.dayRef}
dobRef={this.dobRef}
}
}
And in the child component pass each ref to the related input element:
<form onSubmit={props.handleUpdate}>
<input
~~~
name="name"
~~~
ref={props.nameRef}
~~~
/>
<input
~~~
name="day"
~~~
ref={props.dayRef}
~~~
/>
<input
~~~
name="dob"
~~~
ref={props.dobRef}
~~~
/>
Also, remember you should use separated refs for each input elements, not using one to all of them.

ReactJS: Why does my textarea value always render invisible?

Trying to set up something simple.
Parent: app.js
class App extends React.Component {
constructor(props) {
super(props);
//This acts as our global state
this.state = {
username: "",
email: "",
bio: ""
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
username: "jonny",
email: "jonny#mail.com",
bio: "My bio...."
});
}, 5000);
}
handleFormChange = data => {
this.setState(data);
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Form data={this.state} onHandleFormChange={this.handleFormChange} />
<p>Name from App state: {this.state.username}</p>
<p>Email from App state: {this.state.email}</p>
<p>Bio from App state: {this.state.bio}</p>
</div>
);
}
}
Child: form.js
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
...this.props.data
};
}
handleSubmit = e => {
e.preventDefault();
};
handleChange = e => {
this.props.onHandleFormChange({ [e.target.name]: e.target.value });
};
// static getDerivedStateFromProps(nextProps, prevState) {
// console.log(nextProps.data)
// return {
// ...nextProps.data
// };
// }
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.setState({ ...this.props.data });
}
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="username"
defaultValue={this.state.username}
onChange={this.handleChange}
/>
<input
type="email"
name="email"
defaultValue={this.state.email}
onChange={this.handleChange}
/>
<textarea
name="bio"
defaultValue={this.state.bio}
onChange={this.handleChange}
/>
<input type="submit" value="submit" />
</form>
</div>
);
}
}
I created an artificial API call by using a setTimeout() in this example and I'm trying to set the state of the parent with the result of the API call. Then I wish to pass that as a prop to the child...
It's working except in the case of a textarea. I can see it if I inspect the DOM but it doesn't actually show in the browser...
Note the "my bio..." in the inspector, but the textarea being empty in the browser.
I've tried componentWillUpdate(), componentDidUpdate() and getDerivedStateFromProps() but nothing seems to work.
What am I missing?
Note: I am not using value="" because then it stops me typing and this form is supposed to allow you to update existing values
Sandbox... https://codesandbox.io/s/ancient-cloud-b5qkp?fontsize=14
It seems to work fine by using the value attribute instead of defaultValue. The defaultValue attribute should really only be used sparingly, since you almost always want your inputs to connect to component state. The optimal way to create a controlled input is by using value.
<textarea
name="bio"
value={this.state.bio}
onChange={this.handleChange}
/>
Change the defaultValue in textarea to value

Uncaught TypeError: Cannot read property 'value' of undefined in REACT JS

I am creating a login form using REACT JS as front-end and PHP as back-end. I am trying to get input values. Code is as below:
import React, { Component} from 'react';
import ReactDOM from 'react-dom';
import {Button, IconButton} from 'react-toolbox/lib/button';
import Input from 'react-toolbox/lib/input';
export default class Login extends React.Component {
constructor() {
super();
this.state = {email: ''};
this.state = {password: ''};
this.onSubmit = this.onSubmit.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
}
handleEmailChange(e) {
this.setState({email: e.target.value});
}
handlePasswordChange(e) {
this.setState({password: e.target.value});
}
onSubmit() {
fetch('http://xyz/check-login', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: this.state.email,
password: this.state.password,
})
})
}
And form is as below:
<form name="Login">
<Input type="text" name="email" value={this.state.email} placeholder="Email Id" className="form-control" onChange={this.handleEmailChange} />
<Input name="password" value={this.state.password} placeholder="Password" type="password" className="form-control m-b-10" onChange={this.handlePasswordChange} />
<Button type="button" className="m-t-20 orange" label="Sign in " onClick={this.onSubmit} />
</form>
But getting the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
I am using react-toolbox. So, I am using component from https://github.com/react-toolbox/react-toolbox/blob/dev/components/input/Input.js and component from https://github.com/react-toolbox/react-toolbox/blob/dev/components/button/Button.js.
First of all, what are <Input ..../> and <Button .../>? Are they your components or they are only form input fields?
I suppose that they are only form fields, thus they need to be in lower case <input ..../> , <button .../>.
Try to bind your functions inside render, like : this.functionName.bind(this).
This is a working code :
class Test extends React.Component {
constructor(props){
super(props);
this.state = {
email: '',
password: '',
};
}
handleEmailChange(e) {
this.setState({email: e.target.value});
}
handlePasswordChange(e) {
this.setState({password: e.target.value});
}
render(){
return (
<div>
<form name="Login">
<input type="text" name="email" value={this.state.email} placeholder="Email Id" className="form-control" onChange={this.handleEmailChange.bind(this)} />
<input name="password" value={this.state.password} placeholder="Password" type="password" className="form-control m-b-10" onChange={this.handlePasswordChange.bind(this)} />
<button type="button" className="m-t-20 orange" label="Sign in " onClick={this.onSubmit}>Sign in</button>
</form>
</div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
UPDATE
I tested it here :
constructor(props){
super(props);
this.state = {
name: '',
email: ''
}
}
handleChange(name, value){
let state = this.state;
state[name] = value;
this.setState({state});
}
render () {
return (
<section>
<Input type='text' label='Name' name='name' value={this.state.name} onChange={this.handleChange.bind(this, 'name')} maxLength={16} />
<Input type='email' label='Email address' icon='email' value={this.state.email} onChange={this.handleChange.bind(this, 'email')} />
</section>
);
}
I'm not sure how it works, but it pass name and value as params to the handleChange function, thus, you can get value as a second param. You don't need event.
First change your this.state in constructor to single - this.state = {emai:'',password:''}, then try to bind handleEmailChange and handlePasswordChange inside of input instead in constructor, u need to set this direct to input,
UPDATE
or if Input and Button are components, u need implement changeMethod and onChange event in them, and send value back to component Login via callback
HOW IT WORKS -
class Input extends React.Component{
constructor(props){
super(props);
this.state= {
value : this.props.value
}
}
componentWillReceiveProps(nextProps){
this.setState({
value: nextProps.value,
})
}
render(){
return(
<input onChange={this.handleChangeValue.bind(this)} type="text" name={this.props.name} value={this.state.value} placeholder={this.props.placeholder} className={**just class name or send via props too**} />
)
}
handleChangeValue(e){
this.setState({value:e.target.value});
this.props.changeValue(e.target.value);
}
}
class Login extends React.Component{
constructor(props){
super(props);
this.state= {
emailValue : '',
passwordValue: '',
...
}
}
render(){
return(
<div>
<Input type="text" name='email' value={this.state.emailValue} placeholder={'Write email'} className='someName' changeValue={this.changeEmailValue.bind(this)} />
<Input type="text" name='password' value={this.state.passwordValue} placeholder={'Write password'} className='someName' changeValue={this.changePasswordValue.bind(this)} />
</div>
)
}
changeEmailValue(value){
this.setState({emailValue:value});
}
changePasswordValue(value){
this.setState({passwordValue:value});
}
}
Since you are using a custom component <Input/>, it is likely whatever special code that component has is abstracting away the default event passed from the built-in component <input/>. (note the lower case "i") So maybe you need it to read:
handleEmailChange(value) {
this.setState({email: value});
}
handlePasswordChange(value) {
this.setState({password: value});
}
Regardless the fix is likely that onChange is not returning an event, but some other value you were not expecting.
Instead on binding events in the constructor bind it with the input fields, it will solve your problem.
one more suggestion: you are using two state object, Don't use two separate state variable initialisation, define it like this:
this.state = {
email: '',
password: '',
};
If you use Formik, You should set initialValues for it.
<Formik
initialValues={{
email: '',
password: '',
}}
onSubmit={(values) => {
console.log("formik", values)
}}
>

Categories

Resources