React: Function in Parent Component not receiving data from Child Component - reactjs

I'm working on a project using React where I need to update the state of the Parent Component with the Child Component's input. I did console.log() my way through each function in the chain and found that the fetchText() function in my parent component didn't receive the text
Here's what my parent component looks like
class AppComponent extends React.Component{
constructor(props){
super(props);
this.state = { markdownText: `` };
this.fetchText = this.fetchText.bind(this);
}
fetchText(text){
this.setState({ markdownText: text });
console.log(text);
}
render(){
return(
<div id="app-grid">
<h1>Markdown Viewer</h1>
<MarkDownComponent userInput={this.fetchText} />
<PreviewComponent />
</div>
);
}
}
My Child Component looks like this
class MarkDownComponent extends React.Component{
constructor(props){
super(props);
this.state = { text: ``};
this.getInput = this.getInput.bind(this);
this.sendInput = this.sendInput.bind(this);
}
getInput(event){
this.setState({ text: event.target.value });
this.sendInput();
}
sendInput(){
this.props.userInput = this.state.text;
//console.log(this.props.userInput);
}
render(){
return(
<div id="markdown-component">
<textarea id="editor" rows="16" onChange={this.getInput}></textarea>
</div>
);
}
}
When console.log()ing this.props.userInput in the Child Component I get the value back as I type. So that indicates the value is making it to the property, but why isn't it updating in the parent component?

Few things to note here:
you cannot change the value of props, it is passed to the component through it parent
this.props.userInput = this.state.text;
this won't work.
So, to make fetchData of parent get the text from textarea you should do like this
<textarea id="editor" rows="16" onChange={this.props.userInput}></textarea>
and in parent component :
fetchText(event){
console.log(event.target.value)
this.setState({ markdownText: event.target.value });
}
you don't require functions like getInput and sendInput to send data to the parent component.

The issue is you are assigning state value to a function which is not correct.
this.props.userInput = this.state.text; // this is incorrect
//Right one
class MarkDownComponent extends React.Component{
constructor(props){
super(props);
this.state = { text: ``};
this.getInput = this.getInput.bind(this);
this.sendInput = this.sendInput.bind(this);
}
getInput(event){
this.setState({ text: event.target.value });
this.sendInput();
}
sendInput(){
this.props.userInput(this.state.text);
//console.log(this.props.userInput);
}
render(){
return(
<div id="markdown-component">
<textarea id="editor" rows="16" onChange={this.getInput}></textarea>
</div>
);
}
}
You can directly call this.props.userInput function in getInput function:
class MarkDownComponent extends React.Component{
constructor(props){
super(props);
this.state = { text: ``};
this.getInput = this.getInput.bind(this);
}
getInput(event){
this.props.userInput(event.target.value);
}
render(){
return(
<div id="markdown-component">
<textarea id="editor" rows="16" onChange={this.getInput}></textarea>
</div>
);
}
}
ES6 way:
class MarkDownComponent extends React.Component{
constructor(props){
super(props);
this.state = { text: ``};
}
getInput = (event) => {
this.props.userInput(this.state.text);
}
render(){
return(
<div id="markdown-component">
<textarea id="editor" rows="16" onChange={this.getInput}></textarea>
</div>
);
}
}

As told in the comments, there is no need to assign a state to your function. Also, if your desire is to change the text with a Child and nothing more you don't need a state in your Child. Don't use state if it is not necessary.
class AppComponent extends React.Component {
constructor(props) {
super(props);
this.state = { markdownText: "" };
this.fetchText = this.fetchText.bind(this);
}
fetchText(e) {
this.setState({ markdownText: e.target.value});
}
render() {
return (
<div id="app-grid">
<h1>Markdown Viewer</h1>
Value is now: {this.state.markdownText}
<MarkDownComponent userInput={this.fetchText} />
</div>
);
}
}
const MarkDownComponent = ( props ) => {
return (
<div id="markdown-component">
<textarea id="editor" rows="16" onChange={props.userInput}></textarea>
</div>
)
}
ReactDOM.render(<AppComponent />, 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>

Related

React - State change of parent does not cause re-rendering of child when passed down as props?

I have been trying to learn React lately and this issue has really confused me. Here's the code:
I have created two class components: App, representing the parent and Child, representing the child.
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
randomize() {
this.setState({value: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
The App component contains a button which increments its state value by 1 every time it is clicked. The Child component contains a button which changes its state value to a random number between 0 and 100 every time it is clicked.
While running this, I expected the Child to be re-rendered every time myState in the parent changed, since that is what I passed down to it as props. I also expected this.state.value in the Child to change because I set it to be myState. However, when I incremented myState, the Child is completely unaffected and still displays whatever randomize() gave to it before.
Can anyone show me what's wrong? Thanks :)
You have the following snippet in your Child constructor:
this.state = {
value: this.props.value
}
The above only sets the Child state once, before the component is mounted. Therefore, none of your increments/decrements push through from your parent to child.
The best solution to your problem is to simply keep the state in your parent component, and only pass down a function that can update it.
Try this:
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
this.updateState = this.updateState.bind(this)
}
updateState(newState) {
this.setState(newState);
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState} update={this.updateState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.randomize = this.randomize.bind(this)
}
randomize() {
this.props.update({myState: Math.random() * 100})
}
render() {
return(
<div>
<h1>My value is {this.props.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
You are missing component lifecycle. constructor of a class called once only, not on every re-render.
If u wants to change the data of child wrt parent, then use componentDidUpdate(), which will get called whenever data is updated.
refer here
Modified Code for child:
class App extends React.Component {
constructor() {
super()
this.state = {
myState: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState((previousState) => ({myState: previousState.myState + 1}), () => console.log(this.state.myState))
}
render() {
return (
<div>
<Child value={this.state.myState}/>
<button onClick={this.handleClick}>This updates the state of App (parent)</button>
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
value: this.props.value
}
this.randomize = this.randomize.bind(this)
}
randomize() {
this.setState({value: Math.random() * 100})
}
componentDidUpdate(prevProps, prevState) {
if (this.props.value !== prevProps.value) {
this.setState({value: this.props.value + prevState.value - prevProps.value})
}
}
render() {
return(
<div>
<h1>My value is {this.state.value}</h1>
<button onClick={this.randomize}>This changes the state of Child</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>

ReactJS: pass data through 3 components

I'm trying to pass data through 3 components within the hierarchy (Parent-Child-Child2):
Parent-class:
class Parent extends React.Component {
constructor(props) {
super(props);
this.handleOperation = this.handleOperation.bind(this);
}
handleOperation = (value) => {
// some actions
};
render() {
return (
<div className={styles}>
<Child valueChild={this.handleOperation()}/>
</div>
);
}
}
Child1:
class Child extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange =(value) => {
this.props.valueChild(value);
};
render() {
return (
<div>
<Child2 childValue2 = {this.handleChange} />
</div>
);
}
}
Child2:
class Child2 extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange =(event) => {
this.props.childValue2(event.target.value);
};
render() {
return (
<div>
<input type="button" onClick={this.handleChange} value="defaultValue" />
</div>
);
}
}
Code compiling successfully, but when I click on input-field (to pass value to Parent), I receive the following error:

Passing props from child to parent class component in React

My child class is ChangeProps and parent is ParentClass
class ChangeProps extends React.Component{
render(){
return(
<div>
<h2>{this.props.childname}</h2>
<button onClick={()=>this.props.onNameChange()}>Change</button>
</div>
);
}
}
class ParentClass extends React.Component{
constructor(props){
super(props);
this.state={
name:'parent name'
}
this.onChange=this.onChange(this);
}
onChange=(newName)=>
{
this.setState({ name: newName });
}
render(){
return(
<div>
<ChangeProps childname={this.state.name} onNameChange={this.onChange}/>
</div>
);
}
}
When I click on the button to change the state, name doesn't change. I got the below error:
TypeError: this.props.onNameChange is not a function
since you are using arrow functions you dont have to explicitly define the scope so commenting the line this.onChange=this.onChange(this); would work
class ParentClass extends React.Component{
constructor(props){
super(props);
this.state={
name:'parent name'
}
//this.onChange=this.onChange(this);//comment this line
}
onChange=(newName)=>
{
this.setState({ name: newName });
}
render(){
return(
<div>
<ChangeProps childname={this.state.name} onNameChange={this.onChange}/>
</div>
);
}
}
Here is the working demo
Or else
class ParentClass extends React.Component{
constructor(props){
super(props);
this.state={
name:'parent name'
}
this.onChange = this.onChange.bind(this);//bind the function to the scope
}
onChange(newName) {//keep this as regular function
this.setState({ name: newName });
}
render(){
return(
<div>
<ChangeProps childname={this.state.name} onNameChange={this.onChange}/>
</div>
);
}
}

Triggering a parent function from child using props

I am trying to do something simple: I want my child component to trigger a function found in my parent component, and I understand the right way is using props.
In the following Codepen you can find an example:
https://codepen.io/akmur/pen/MvXGEG
Basically what I want to achieve is to print "hey" to console.
This is my code:
class Form extends React.Component {
constructor(props){
super(props);
}
onClickAdd(){
this.props.addItem();
}
render(){
return(
<div>
<button onClick={this.onClickAdd}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props){
super(props);
}
addItem(){
console.log('hey');
}
render() {
return (
<div>
<Form addItem={this.addItem} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Thanks for your help!
You don't need the onClickAdd function. Just call this.props.addItem directly onClick (notice, no parens) that you passed down.
class Form extends React.Component {
constructor(props){
super(props);
}
render(){
return(
<div>
<button onClick={this.props.addItem}>Add</button>
</div>
)
}
}
class App extends React.Component {
constructor(props){
super(props);
}
addItem(){
console.log('hey');
}
render() {
return (
<div>
<Form addItem={this.addItem} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));

Getting error Cannot read property 'setState' of undefined

I'm new with Reactjs. I'm trying to do something very simple: update a div inside a render function when the user change the text inside text area. Any suggestions?
class HTMLEditor extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Put here HTML'};
}
handleChange(e) {
this.setState({value: e.currentTarget.value});
}
render() {
return (
<div>
<textarea defaultValue={this.state.value} onChange={ this.handleChange } />
<div>{this.state.value}</div>
</div>
);
}
}
ReactDOM.render(
<HTMLEditor />,
document.getElementById('container')
);
You should bind the handleChange function. You are getting this error because, in your handleChange function this keywork doesn't refer to the context of the React Class and hence you need to bind the function.
See this answer on why do you need to bind event handlers in React
class HTMLEditor extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Put here HTML'};
}
handleChange = (e) =>{
this.setState({value: e.currentTarget.value});
}
render() {
return (
<div>
<textarea defaultValue={this.state.value} onChange={ this.handleChange } />
<div>{this.state.value}</div>
</div>
);
}
}
ReactDOM.render(
<HTMLEditor />,
document.getElementById('container')
);
<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="container"></div>

Resources