0

I've made a simple react tasklist, which allows me to add new tasks. I now want to be able to remove tasks, but cant see how to pass a function property down to a child / grandchild component.

I'm wanting to pass the deleteTaskFromState function property down to the <Task /> component. I've successfully done this with the addTaskToState function, but cant see how to do it with the deleteTaskFromState function prop.

Codepen here

On line 108 I've pass deleteTaskFromState to the <TaskList /> component, but when I put deleteTask={this.props.deleteTaskFromState} on the <Task /> component (line 57) it errors with Cannot read property 'props' of undefined.

If anyone can help I'd be hugely grateful!

After this property is passed I need to then complete the function prop deleteTaskFromState to remove the selected task from state, if anyone know how to do that too, that would be great!

// function to generate unique id for react keys - use npm uuid usually
function guid() {
  function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  }
  return s4() + s4() 
    // + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}

class TaskInput extends React.Component {
    createTask(event){
        event.preventDefault();
        let task = {
            key: guid(),
            task : this.refs.task.value,
            priority : this.refs.priority.value,            
        }       
        this.props.addTaskToState(task);        
        this.refs.addTaskForm.reset();
    }

    render() {
        return (
            <form onSubmit={this.createTask.bind(this)} ref="addTaskForm">
                <input placeholder="enter a task" ref="task" />
                <input placeholder="priority" ref="priority" />
                <button type="submit">Submit</button>               
            </form>
        );
    }
}

class Task extends React.Component {
    render() {
        return (
            <div>
                <li className="task"><button>x</button> 
                <strong> {this.props.taskdata.priority} </strong>  
                {this.props.taskdata.task}</li>
            </div>
        );
    }
}

class TaskList extends React.Component {
    render() {
        if(this.props.deleteTaskFromState) {
            console.log('deleteTaskFromState found')
        }

         let TaskItems;
         if(this.props.taskstate){
            TaskItems = this.props.taskstate.map(function (taskdata)  {
                return (
                    <Task taskdata={taskdata} key={taskdata.key}/>
                );
            });
         }

        return (
            <ul className="task-list">
                {TaskItems}
            </ul>
        );
    }
}

class TaskListApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            tasks : [ 
            { 
                key: "task1",
                task: "dance",
                priority: "superhigh",
              },
            { 
                key: "task2",
                task: "tax return",
                priority: "superlow",
            }
            ]
        };
    }

    deleteTaskFromState(keyToDelete) {
        let tasks = this.state.tasks;
        // find object in state array with keyToDelete 
        // remove object
    }

    addTaskToState(task) {
        let tasks = this.state.tasks;
        tasks.push(task);
        this.setState({tasks:tasks});
    }

    render() {
        return (
            <div>
                <h1>Task List</h1>
                <TaskInput addTaskToState={this.addTaskToState.bind(this)} />
                <TaskList 
                    taskstate={this.state.tasks} 
                    deleteTaskFromState={this.deleteTaskFromState.bind(this)}  
                    />
            </div>
        );
    }
}

ReactDOM.render(<TaskListApp />, document.getElementById("app"));
Ash
  • 228
  • 6
  • 21

1 Answers1

1

All you have to do just pass deleteTaskFromState

to child/grandchild like you did with addTaskToState

CodePen

class Task extends React.Component {
    deleteTask() {
     this.props.deleteTaskFromState(this.props.taskdata.key)
   }

    render() {
        return (
            <div>
                <li className="task"><button onClick={this.deleteTask.bind(this)}>x</button>
                <strong> {this.props.taskdata.priority} </strong>  
                {this.props.taskdata.task}</li>
            </div>
        );
    }
}

class TaskList extends React.Component {
    render() {
        if(this.props.deleteTaskFromState) {
            console.log('deleteTaskFromState found')
        }

         let TaskItems;
         if(this.props.taskstate){
       let deleteTaskFromState = this.props.deleteTaskFromState;
            TaskItems = this.props.taskstate.map(function (taskdata)  {
                return (
                    <Task
   taskdata={taskdata}
   key={taskdata.key}
   deleteTaskFromState={deleteTaskFromState} />
                );
            });
         }

        return (
            <ul className="task-list">
                {TaskItems}
            </ul>
        );
    }
}
chinnawatp
  • 1,430
  • 16
  • 15
  • Oh, you store the prop in a variable and pass it through the .map function? I tried passing `deleteTaskFromState` to the `` component by putting `deleteTask={this.props.deleteTaskFromState}` on the `` component (line 57), but it errored with `cannot read property 'props' of undefined`. Is it because the keyword `this` was looking for the prop on the `` element, which didn't have that prop? Thanks so much for the help! :D – Ash Jun 04 '17 at 16:32
  • 1
    `cannot read property 'props' of undefined` because `this` is undefined. `this` is undefined because `this` context inside callback does not refer to component. That is why I store the prop in a variable first I think this link should provide better explanation https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback – chinnawatp Jun 04 '17 at 17:10