Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call parent function from child prop React 15.5.0

Hello I was trying to pass function to child class and call it from the child class but the problem it show the the function is undefined

Cannot read property 'removeComment' of undefined

here is my codes

parent class:

import React from 'react';
import Navbar from './notes';

export default class Board extends React.Component {
    constructor(props, context) {
      super(props, context);
        this.state = {
            comments: [
              'Kingyyy',
              'React',
              'Learning'
            ]
        };
    }
    removeComment() {
      console.log(i);
      var arr = this.state.comments;
      arr.splice(i,1);
      this.setState({comments: arr});
    }
    editComment(newtext, i) {
      console.log(i);
      var arr = this.state.comments;
      arr[i] = newtext;
      this.setState({comments: arr});
    }
    addComment() {
      var text = prompt('enter the new ');
      var arr = this.state.comments;
      arr[arr.length] = text;
      this.setState({comments: arr});
    }
    eachComment(elem, i) {
        return (
            <Navbar key={i} index={i} editComment={(newtext, i) => this.editComment.bind(this)} removeComment={(i) => this.removeComment.bind(this)}>
              {elem}
            </Navbar>
        );
    }
    render() {
        return (
            <div>
            <button onClick={this.addComment} className="btn btn-success">add new comment</button>
            <br />
                {
                  this.state.comments.map(this.eachComment)
                }
            </div>
        );
    }
}

the child class:

import React from 'react';

export default class Navbar extends React.Component {
    edit() {
        this.setState({
            edit: !this.state.edit
        })
    }
    save() {
        var value = this.refs.newtext.value;
        this.props.editComment(value,this.props.index);
        this.setState({
            edit: !this.state.edit
        })
    }
    remove() {
      this.props.removeComment(this.props.index);
    }
    constructor(props, context) {
      super(props, context);
      this.state = {edit: false};
    }
    normal() {
        return (
            <div>
                <h1>{this.props.children}</h1>
                <button className="btn btn-info" onClick={this.edit.bind(this)}>
                    edit
                </button>
                <button className="btn btn-danger" onClick={this.remove.bind(this)}>
                    remove
                </button>
            </div>
        );
    }
    editing() {
        return (
            <div>
                <textarea ref="newtext" defaultValue={this.props.children}></textarea>
                <br/>
                <button className="btn btn-success" onClick={this.save.bind(this)}>
                    save
                </button>

            </div>
        );
    }
    render() {
        if (this.state.edit) {
            return this.editing();
        } else {
            return this.normal();
        }
    }
}
like image 953
Ameer Fakhri Avatar asked Nov 25 '25 01:11

Ameer Fakhri


1 Answers

the issue is you are losing your react context. Change your constructor for the child class to this

constructor(props, context) {
    super(props, context);
    this.state = {edit: false};
    this.normal = this.normal.bind(this)
    this.editing = this.editing .bind(this)
}

you call .bind(this) on your remove call... however the this you are binding doesn't have the react context with state and props

A few optimizations I would suggest..

define your functions as inline lambdas so you dont have to call .bind(this) on every function every time... aka

edit = () => {
    this.setState({
        edit: !this.state.edit
    })
}
save = () => {
    var value = this.refs.newtext.value;
    this.props.editComment(value,this.props.index);
    this.setState({
        edit: !this.state.edit
    })
}
remove = () => {
  this.props.removeComment(this.props.index);
}
normal = () => {
    return (
        <div>
            <h1>{this.props.children}</h1>
            <button className="btn btn-info" onClick={this.edit}>
                edit
            </button>
            <button className="btn btn-danger" onClick={this.remove}>
                remove
            </button>
        </div>
    );
}
editing = () => {
    return (
        <div>
            <textarea ref="newtext" defaultValue={this.props.children}></textarea>
            <br/>
            <button className="btn btn-success" onClick={this.save}>
                save
            </button>

        </div>
    );
}

in the parent class change how you pass the functions. Try to always avoid inline lambdas as properties on an element or react class (in the render). It will cause performance issues as the site gets more complex.

eachComment = (elem, i) => {
    return (
        <Navbar key={i} index={i} editComment={this.editComment} removeComment={this.removeComment}>
          {elem}
        </Navbar>
    );
}

if you needed to pass custom variables to a function that were defined inline you can use .bind to pass them through instead of a lambda (bind is a lot more performant than a lambda... aka

someList.map( (item, i) => <SomeElement onUserClick={this.handleUserClick.bind(null, item) />);

.bind()'s first argument is the context this you can pass null to not override the context. and then you can pass any other arguments to the function to be arguments extended on the invoked call.

like image 79
John Ruddell Avatar answered Nov 27 '25 15:11

John Ruddell



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!