I'm trying to create a controlled text area.
class TextArea extends React.Component {
constructor(props) {
super(props);
this.state= {
text: this.props.initial
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
//some handle
}
render() {
return (
<textarea
value={this.state.text}
placeholder={this.props.initial}
onChange={this.handleChange}
/>
);
}
}
For some reason if I console.log the this.props.initial in the constructor I get an undefined.
But the placeholder works.
What I would like to do is to ditch the placeholder and set an initial value to that the user can edit and copy and interact with. (basically normal text and not a placeholder, but I cannot do that because it doesn't work)
What am I doing wrong?
Edit:
The way that I am passing props.initial to the textarea:
<TextArea
initial={this.state.json.initial}
text={this.state.json.text}
changeHandler={this.handleChange}
/>
I am getting the json from a $.getJSON call and I think that the textarea gets rendered before the json call is finished. Is there any way to run the render function only after the componentWillMount function?
Remove this from this.props in the constructor since you have access to props from its argument list.
class TextArea extends React.Component {
constructor(props){
super(props)
this.state = {
text: props.initial,
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event){
this.setState({ text: event.target.value })
}
render(){
return (
<div>
<div>Initial text: {this.props.initial}</div>
<textarea
value={this.state.text}
placeholder={this.props.initial}
onChange={this.handleChange}
/>
</div>
)
}
}
ReactDOM.render(
<TextArea />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You have to garantee that this.props.inital exists:
{ this.state.json && this.state.json.initial &&
<TextArea initial={this.state.json.initial} text={this.state.json.text} changeHandler={this.handleChange}/>
}
the important part to understand is that the constructor already takes in the props as parameters so you can access props directly in the constructor as props. no need to access it via this.props as long as you are inside the constructor
constructor(props) {
super(props);
this.state= {
text: props.initial
}
}
the code above should work
However, this is a better way of structuring a component like TextArea and it should also solve your problem of props.initial not having a value on runtime
First, you need to prepare the handleChange method in the parent component
class ParentComponent extends Component {
constructor(props) {
super(props)
this.state = {
myTextArea: ''
}
this.handleChange = this.handleChange.bind(this)
}
handleChange (e) {
this.setState({myTextArea: e.target.value})
}
render () {
return (
<TextArea
value={myTextArea}
onChange={this.handleChange}
/>
)
}
}
and on the text area component, you refer to onchange method passed through the props when defining the onchange method of your textarea.
<textarea
value={this.props.value}
placeholder="Something"
onChange={this.props.handleChange}
/>
the benefit of this approach is that one, the one that calls the textarea will always have an updated value, and two, this child element doesnt need to have a state. it makes managing a large react app easier and its the correct mind set to have once you start trying to implement Redux or similar frameworks to handle your state for you
Related
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
passing data from child to parent component via callback function
but somehow it's not working.
what am I doing wrong here?
passing data from child to parent component - react - via callback function
https://codepen.io/silentarrowz/pen/GEMQEP?editors=0010
and here's the code
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
//this.handleInput=this.handleInput.bind(this);
}
handleInput(x){
this.setState({
input:x
});
alert(this.state.input);
}
render(){
return(
<div>
<h1>Passing props from Child to Parent Component</h1>
<Child getInput={this.handleInput} />
here's the input: {this.state.input}
</div>
);
}
}
class Child extends React.Component{
constructor(){
super();
this.state={
text:''
}
}
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.passingProps} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
There are a couple of issues.
1) You have to bind passingProps
constructor(){
super();
this.state={
text:''
}
this.passingProps = this.passingProps.bind(this);
}
2) this.setState is asynchronous, so it's not guaranteed that this.state.text will be set to the value you want by the time you pass it to this.props.getInput. You can either do
this.props.getInput(newInput)
or
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
to resolve that issue.
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
this.handleInput=this.handleInput.bind(this);
}
handleInput(event){
let value = event.target.value;
this.setState({
input:value
});
}
render(){
return(
<div>
<h1>{this.state.input}</h1>
<Child getInput={this.handleInput} />
</div>
);
}
}
class Child extends React.Component{
constructor(){
super(props);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.props.getInput} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
Here is the answer for your question. I hope your proplem is solved.
In your Child Component, you have written following code:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
The issue is due to the asynchronous behaviour of setState function. It means you can not call setState on one line and expect its updates on next line.
Use the callback function of setState to call the function of parent component just like this:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
}
Same thing is happening in handleInput function of App component.
this is not automatically bound in your passingProps function. Try arrow function syntax to bind it.
passingProps = e => {
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
Two things that you need to correct it:
if you want to access new state, you don't use this.state.input after
this.setState({input: 'xxx'}). Here is reason why not it.
this.passingProps = this.passingProps.bind(this) is defined what this is current scope. when you use this in component's function, this need to be bind.
Changed codepen
You can create a method in parent that accepts some data and then sets the received data as parent state.
Then pass this method to child as props. Now let the method accept child state as input and then let the method set the received child state as parent state.
I have a parent,child and grandchild component. I have different input fields and want to pass the values from grandchild to child to parent where eventually i set the state with the values. I havent included all of my code, but doing it like that is necessary because of other things in my code, which I didnt include in this post as its irrelevant. Im not sure how to do that and tried to implement what I found online, however, its not working. Any ideas? Thanks!!
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
input: {}
};
this.changeName = this.changeName.bind(this);
this.handleInput = this.handleInput.bind(this);
}
changeName(newName) {
this.setState({
name: newName
});
}
handleInput() {
console.log("helloooooo", this.state.name)
}
render() {
return (
<div>
<Child onChange={this.changeName} onClick={this.handleInput}/>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
this.props.handleChange(e);
}
handleInput2() {
this.props.onClick()
}
render() {
return (
<div>
<GrandChild onChange={this.handleChange}/>
<input type="submit" onClick={this.handleInput2}/>
</div>
)
}
}
class GrandChild extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleInput2 = this.handleInput2.bind(this);
}
handleChange(e) {
const input = this.props.input;
input[name] = e.target.value;
}
render() {
return (
<div>
<input name="firstname" onChange={this.handleChange}/>
<input name="lastname" onChange={this.handleChange}/>
</div>
)
}
In real life everything is easier. For every component you answer following questions.
What data the component will receive?
Does it emit any event?
That's the props of the component.
So no matter how the relationship between your components is... Just answer those questions and you will be good.
Example:
I have a TodoList that contains a list of TodoItem elements. (Parent)
I have a TodoItem that displays the content of the TodoItem. (Child)
I have a Checkbox that displays a check box. (GrandChild)
a CheckBox receives a boolean saying isSelected and emit and event onChange. That's all what I know.
a TodoItem receives a Todo and emit onChange. That's all I care.
When you put everything together, TodoList has a todos, and pass todos[i] to its child and todos[i].isSelected to its grandchild, but that is what you don't need to care about. All what you care is:
What data the component will receive? Does it emit any event?
At the component level.
passing data from child to parent component via callback function
but somehow it's not working.
what am I doing wrong here?
passing data from child to parent component - react - via callback function
https://codepen.io/silentarrowz/pen/GEMQEP?editors=0010
and here's the code
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
//this.handleInput=this.handleInput.bind(this);
}
handleInput(x){
this.setState({
input:x
});
alert(this.state.input);
}
render(){
return(
<div>
<h1>Passing props from Child to Parent Component</h1>
<Child getInput={this.handleInput} />
here's the input: {this.state.input}
</div>
);
}
}
class Child extends React.Component{
constructor(){
super();
this.state={
text:''
}
}
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.passingProps} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
There are a couple of issues.
1) You have to bind passingProps
constructor(){
super();
this.state={
text:''
}
this.passingProps = this.passingProps.bind(this);
}
2) this.setState is asynchronous, so it's not guaranteed that this.state.text will be set to the value you want by the time you pass it to this.props.getInput. You can either do
this.props.getInput(newInput)
or
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
to resolve that issue.
class App extends React.Component{
constructor(props){
super(props);
this.state={
input:'this is the input for now'
}
this.handleInput=this.handleInput.bind(this);
}
handleInput(event){
let value = event.target.value;
this.setState({
input:value
});
}
render(){
return(
<div>
<h1>{this.state.input}</h1>
<Child getInput={this.handleInput} />
</div>
);
}
}
class Child extends React.Component{
constructor(){
super(props);
}
render(){
return(
<div>
<input type="text" placeholder="please input a name..." onChange={this.props.getInput} />
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('app')
);
Here is the answer for your question. I hope your proplem is solved.
In your Child Component, you have written following code:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
The issue is due to the asynchronous behaviour of setState function. It means you can not call setState on one line and expect its updates on next line.
Use the callback function of setState to call the function of parent component just like this:
passingProps(e){
var newInput=e.target.value;
//alert(newInput);
this.setState({ text: newInput }, () => {
this.props.getInput(this.state.text);
})
}
Same thing is happening in handleInput function of App component.
this is not automatically bound in your passingProps function. Try arrow function syntax to bind it.
passingProps = e => {
var newInput=e.target.value;
//alert(newInput);
this.setState({
text:newInput
});
this.props.getInput(this.state.text);
}
Two things that you need to correct it:
if you want to access new state, you don't use this.state.input after
this.setState({input: 'xxx'}). Here is reason why not it.
this.passingProps = this.passingProps.bind(this) is defined what this is current scope. when you use this in component's function, this need to be bind.
Changed codepen
You can create a method in parent that accepts some data and then sets the received data as parent state.
Then pass this method to child as props. Now let the method accept child state as input and then let the method set the received child state as parent state.
I'm trying to print instantly my input value in the render function.
In the documentation of the concept of state and lifecycle in a React component, I see the use of a constructor with a super(props) as well as this.state.
I get the error below when trying same;
Uncaught TypeError: Cannot read property 'state' of undefined
Below is my code;
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: ''
};
};
handleChange(event) {
this.setState({
text: event.target.value
});
};
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.text}.</h2>
<input type="text" onKeyUp={this.handleChange} />
</div>
);
}
}
How can I fix it?
when you call a function like that, it is called by the window, not by your react object.
To make the function be bound to your react object (and have the ability to use the setState method, you need to use this:
onKeyUp={this.handleChange.bind(this)}
this will bind it to your react object :)
You have to bind this to your event handler like this:
<input type="text" onKeyUp={this.handleChange.bind(this)} />
Working Example: https://codepen.io/shanedaugherty/pen/ALwAzL
this.handleChange = this.handleChange.bind(this);
You can bind it like this in constructor and it will work, as you have to bind this to your react function.
You use value as an attribute.
value={this.state.text}
OR