I am a newbie and recently learned about controlled input in React. Apparently one has to bind the function within the event handler that belongs to the component. Why?
class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
input: ''
};
// binding in the constructor
this.handleChange = this.handleChange.bind(this)
}
// what the function does
handleChange(event){
this.setState({
input: event.target.value
})
}
render() {
return (
<div>
<input value = {this.state.input}
{//binding within the component}
<input onChange = {this.handleChange.bind(this)}>
</input>
{//why}
<h4>Controlled Input:</h4>
<p>{this.state.input}</p>
</div>
);
}
};
Sorry for bothering you
Related
So I started converting my application from ES2015 to ES6 which uses React.
I have a parent class and a child class like so,
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange(newCode) {
this.setState({code: newCode});
}
login() {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child class,
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange(e) {
this.props.onCodeChange(e.target.value);
}
login() {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange.bind(this)}/>
</div>
<button id="login" onClick={this.login.bind(this)}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
However this causes the following error,
this.state is undefined
It refers to,
if (this.state.code == "") {
// Some functionality
}
Any idea what could be causing this ?
You can use arrow function to bind you functions. You need to bind you functions both in child as well as parent components.
Parent:
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
code: ''
};
}
setCodeChange = (newCode) => {
this.setState({code: newCode});
}
login = () => {
if (this.state.code == "") {
// Some functionality
}
}
render() {
return (
<div>
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
</div>
);
}
}
Child
export default class Child extends Component {
constructor(props) {
super(props);
}
handleCodeChange = (e) => {
this.props.onCodeChange(e.target.value);
}
login = () => {
this.props.onLogin();
}
render() {
return (
<div>
<input name="code" onChange={this.handleCodeChange}/>
</div>
<button id="login" onClick={this.login}>
);
}
}
Child.propTypes = {
onCodeChange: React.PropTypes.func,
onLogin: React.PropTypes.func
};
There are other ways to bind the functions as well such as the one you are using but you need to do that for parent component too as <Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
or you can specify binding in the constructor as
Parent:
constructor(props) {
super(props);
this.state = {
code: ''
};
this.setCodeChange = this.setCodeChange.bind(this);
this.login = this.login.bind(this);
}
Child
constructor(props) {
super(props);
this.handleCodeChange = this.handleCodeChange.bind(this);
this.login = this.login.bind(this);
}
I agree with all different solutions given by #Shubham Kathri except direct binding in render.
You are not recommended to bind your functions directly in render. You are recommended to bind it in constructor always because if you do binding directly in render then whenever your component renders Webpack will create a new function/object in bundled file thus the Webpack bundle file size grows. For many reasons your component re-renders eg: doing setState but if you place it in constructor it gets called called only once.
The below implementation is not recommended
<Child onCodeChange={this.setCodeChange.bind(this)} onLogin={this.login.bind(this)} />
Do it in constructor always and use the ref wherever required
constructor(props){
super(props);
this.login = this.login.bind(this);
this.setCodeChange = this.setCodeChange.bind(this);
}
<Child onCodeChange={this.setCodeChange} onLogin={this.login} />
If you are using ES6 then manual binding is not required but if you want you can. You can use arrow functions if you want to stay away with scope related issues and manual function/object bindings.
Sorry if there are any typos I am answering in my mobile
I have a simple React component:
import React from "react";
export default class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>;
}
}
I am then importing this component into another component. This new component has a textbox which onChange event, tries to set a property which is being passed as a property to component one.
import React from "react";
import Car from "./Car";
class Garage extends React.Component {
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
}
handleChange(event) {
this.propOne = event.target.value;
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.propOne} />
</>
);
}
}
export default Garage;
After running this code, I am getting TypeError: Cannot set property 'propOne' of undefined error
It's because you are making a new function and creating a new instance of this. Change it to an arrow function -
handleChange = (event) => {
this.propOne = event.target.value;
}
Or you can bind the this of handleChange to parent's this in the constructor of class
constructor(props) {
super(props);
this.propOne = "Ford";
this.state = { value: "initial Value" };
this.handleChange = this.handleChange.bind(this);
}
Kudos to #Atin for mentioning changing your handleChange function to an arrow function or binding this (I'll use the arrow function in my answer).
You'll also need to set propOne in state, and use this.setState(...) to update the value of propOne so that your component re-renders when propOne changes. When you just change this.propOne, React doesn't know that your component needs to re-render with the updated value, which is why you don't see the effect in Car. -> that's what the state is for. Whenever state is changed (with this.setState), React knows it needs to re-render that component, and it will pass your updated state property down to child components.
Try this for your state:
this.state = { value: "initial Value", propOne: "Ford" };
And this for your handleChange function:
handleChange = (event) => {
this.setState({ propOne: event.target.value });
}
Edit:
Additionally, your code (and the above, fixed code) is setting a property propOne on the state, but you're using this.state.value to update your input text value. Is there any reason you're trying to use two different properties - value and propOne? It seems they're being used for the same purpose, and therefore you can replace propOne with value everywhere like this:
constructor(props) {
super(props);
this.state = { value: "initial Value" };
}
handleChange = (event) => {
this.setState({ value: event.target.value });
}
render() {
return (
<>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
<Car name={this.state.value} />
</>
);
}
Referenced error is thrown because you didn't bind this.
Other than that, you have a bunch of errors in your code, so I tried to address them all at once. If you have further questions, feel free to ask in the comments:
const { render } = ReactDOM
class Car extends React.Component {
render() {
return <h2>I am a {this.props.name}</h2>
}
}
class Garage extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = { value: "initial Value", propOne: 'Ford' }
}
handleChange(val) {
this.setState({value: val, propOne: val})
}
render() {
return (
<div>
<input
type="text"
value={this.state.value}
onChange={e => this.handleChange(e.target.value)}
/>
<Car name={this.state.propOne} />
</div>
)
}
}
render(
<Garage />,
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>
p.s. and I agree with above answers, function components and hooks could make your app look much nicer
Hi I would like to pass data from same child to parent component multiple times.
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
stateVariabes:null
};
// callback from parent
this.props.onSelect(this.state);
}
handleChange(event) {
const value = event.target.value;
this.setState(
{
stateVariabes: value
},
function() {
console.log(this.state);
// callback from parent
this.props.onSelect(this.state);
}
);
}
}
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
childOne: null,
childList: [],
childOther: null
};
}
childCallbackFunction = childData => {
// which child state shall I set here
this.setState({ weekDay: childData });
};
render() {
return (
<div className="App">
<ChildComponent onSelect={this.childCallbackFunction} />
<ChildComponent onSelect={this.childCallbackFunction} />
<ChildComponent onSelect={this.childCallbackFunction} />
<ChildComponent onSelect={this.childCallbackFunction} />
</div>
);
}
}
When I use only of one the ChildComponent the update of state from child to parent works as expected but when I have multiple ChildComponent inside render of Parent the state update does not happen.
Can someone suggest whats the right way to achieve the task?
When using Class components, you need to bind your methods. From React documentation:
In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
So, in your case, your ChildComponent should have a
this.handleChange = this.handleChange.bind(this);
You should also remove this from your constructor, since it is calling your callback on initialization:
// callback from parent
this.props.onSelect(this.state);
Here is a working example: https://codesandbox.io/s/intelligent-night-xlhzj
More information in this link.
This is how it should do it.
child=>
class ChildCom extends Component {
constructor(props) {
super(props);
this.state = {
stateVariabes: null
};
}
componentDidMount() {
this.onSelect()
}
onSelect = () => {
const value = 'some value coming from your event';
this.props.trigger(value);
};
Parent=>
class ParentCom extends Component {
constructor(props) {
super(props);
this.state = {
childOne: null,
childList: [],
childOther: null,
};
}
childCallbackFunction = childData => {
// you can do anything with your child component values here
console.log('child value: ', childData)
};
render() {
return (
<div className='App'>
<ChildCom trigger={this.childCallbackFunction} />
<ChildCom trigger={this.childCallbackFunction} />
<ChildCom trigger={this.childCallbackFunction} />
<ChildCom trigger={this.childCallbackFunction} />
</div>
So basically I'm trying to access a function in a child node (that returns the input of a text-box), from the parent node but i keep on getting an error stating that the function is not a function.
MakeMethod.jsx:22 Uncaught TypeError: i.getState is not a function
at MakeMethod.jsx:22
at Array.map (<anonymous>)
at MakeMethod.getAllInfo (MakeMethod.jsx:22)
at onClick (MakeMethod.jsx:41)
I've tried using the ref prop but it doesn't seem to work with an array of components.
The methods/function used to return the props parameter from the child node is the getState() one.
The method/function used to store the list of inputs from the children is the getAllInfo() one.
Is there something I'm doing drastically wrong? If so Would you be able to point it out to me?
Many thanks in advance!!
The parent class is the MakeMethod:
import React from 'react';
import "./MakeRecipe.css";
import Steps from "./Steps.jsx";
const count=1;
const theSteps=[]; //list of step components i want to access
class MakeMethod extends React.Component {
constructor(){
super();
this.state = {
count : 1,
}
this.AddMethod = this.AddMethod.bind(this);
this.getAllInfo=this.getAllInfo.bind(this);
theSteps.push(<Steps key={this.state.count} count={this.state.count} value={''}/>);
}
//this is the function i want to access all my information from
getAllInfo(){
let method=[];
method=theSteps.map((i)=>
i.getState() //iterates through each Step component and calls on the function getState()
//but it says that the function does not exist
);
this.setState({steps:method});
console.log(method);
}
AddMethod(){
let x=this.state.count+1;
theSteps.push(<Steps key={x} count={x}/>);
this.setState({count:x});
}
render() {
return (
<div key={"method"}>
<div>
{theSteps}
</div>
<button onClick={()=>this.AddMethod()}>Add another method?</button>
<button onClick={()=>this.getAllInfo()}>checking info</button>
</div>
);
}
}
export default MakeMethod;
And the Child component is the Steps:
import React from 'react';
import "./MakeRecipe.css"
const count=1;
class Steps extends React.Component {
constructor(props) {
super();
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getState = this.getState.bind(this);
}
//function that returns the value in the text box
getState(){
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<div id={this.props.count}>
<a>Step: {this.props.count}</a>
<input key={this.props.count} type="text" value={this.state.value} onChange={this.handleChange}/>
<br/>
</div>
<button onClick={()=>this.getState()}/>
</div>
);
}
}
export default Steps;
I think you don't exactly know the React lifecycle and how does really works passage from Parent to Child and vice versa.
This is a pseudo code that puts you in the right direction
import React from 'react';
import Steps from "./Steps";
const count=1;
const theSteps=[]; //list of step components i want to access
class MakeMethod extends React.Component {
constructor(props) {
super(props);
this.state = {
count : 1,
theSteps:[] ,
itemFromChild:''
}
this.AddMethod = this.AddMethod.bind(this);
this.getAllInfo=this.getAllInfo.bind(this);
this.state.theSteps.push(<Steps key={this.state.count} count={this.state.count} value={this.getAllInfo}/>);
}
//this is the function i want to access all my information from
getAllInfo=(itemFromChild)=> {
let method=[];
this.setState({ itemFromChild });
console.log(itemFromChild);
}
getAllInfoLocal() {
console.log(this.state.itemFromChild);
}
AddMethod(){
let x=this.state.count+1;
theSteps.push(<Steps key={x} count={x}/>);
this.setState({count:x});
}
render() {
return (
<div key={"method"}>
{this.state.itemFromChild}
<div>
{this.state.theSteps}
</div>
<button onClick={()=>this.AddMethod()}>Add another method?</button>
<button onClick={()=>this.getAllInfoLocal()}>checking info</button>
</div>
);
}
}
export default MakeMethod;
the other class...:
class Steps extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.getState = this.getState.bind(this);
}
//function that returns the value in the text box
getState(){
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
handleChange = (event) =>{
console.log(event.target.value)
this.setState({value: event.target.value});
this.props.value(event.target.value);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<div>
<div id={this.props.count}>
<a>Step: {this.props.count}</a>
<input key={this.props.count} type="text" value={this.state.value} onChange={this.handleChange}/>
<br/>
</div>
<button onClick={()=>this.getState()}/>
</div>
);
}
}
export default Steps;
Hope it helps...
Try to console log your component see what it will display.
Try i.getState. I think it should work since you're calling the property which is a function and not the callback of the function.
Also bind functions to the components like this:
getState = () => {
debugger;
console.log(this.state.value); //tested and works
return this.state.value;
}
since .bind has a lot of memory leaks.
I am new to ReactJs.I tried a small snippet with React.But this.state is not working in ES6 ReactJs.Help me what I am missing!!
JS without ES6:
var App = React.createClass({
getInitialState: function() {
return {value:''};
},
handleClick: function(e){
this.setState({value:e.target.value});
console.log(this.state.value);//getting value here
},
render: function() {
return(
<div>
Hello {this.props.name}
<input type="text" onChange={this.handleClick}/>
{this.state.value}// no value
</div>);
}
});
React.render(<App name="Praveen" />, document.getElementById('content'));
This case is working fine. Jsbin code
JS with ES6:
class App extends React.Component {
constructor(props){
super(props)
this.state ={value:''};
}
handleClick(e){
this.setState({value:e.target.value});
console.log(this.state.value);//getting value here
}
render() {
return(
<div>
Hello {this.props.name}
<input type="text" onChange={this.handleClick}/>
{this.state.value}// no value
</div>);
}
}
React.render(<App name="Praveen" />, document.getElementById('content'));
In this case whenever I am setting this.setState() render function is not called. Jsbin code
React in ES6 removed auto binding of this which means that functions declared on a class that extends React.Component must either .bind(this) explicitly or use arrow function syntax.
<input type="text" onChange={this.handleClick.bind(this)} />
or
class App extends React.Component {
handleClick = (e) => {}
}
For performance reasons, it is recommended to do the binding in the constructor instead so that the binding only happens upon initialization instead of on every render.
Read more about event handlers on React docs here.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {value:''};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
this.setState({value:e.target.value}, () => {
console.log(this.state.value); // Updated value here
});
}
render() {
return(
<div>
Hello {this.props.name}
<input type="text" onChange={this.handleClick}/>
{this.state.value}// no value
</div>
);
}
}