Search code examples
reactjscallbacknested

How do I get a value from a child in grand parent?


EDIT : Solution found. I forgot to had a prop 'handleCallback' when creating my subList

 renderSubList() {
        let subList = this.props.item.subList
        if (subList === undefined)
            return
        subList = subList.map(
            item =>
            <Item key={item.id} item={item} handleCallback={...} /> <== HERE
            )
          return subList

    }

I'm a bit new with Reactjs so I'm trying to do a custom TODO list with task and subtask. I'm facing a problem when I want to delete a subtask telling me that my callback isn't a function. I have no issue when deleting a task. I don't really understand my problem, is it a callback problem or maybe my data structure?

P.S: this is my first post, it is a bit ugly.

github repo for the code : https://github.com/JohnPAfr/todolist

So my data looks like this:

todoList:[
        {id:0, task:'tâche #1', subList: [
            {id:0, parent:0, task:'sous-tâche #1'},
            {id:1, parent:0, task:'sous-tâche #2'},
            {id:2, parent:0, task:'sous-tâche #3'},
        ]},
        {id:1, task:'tâche #2'},
        {id:2, task:'tâche #3'},
        {id:3, task:'tâche #4'},
]

My problem comes when I want to delete an item from the subList.

I map my 'todoList' from my state and make a TodoItem for each object in my list.

class TodoBis extends Component {
  constructor(props){
    super(props)
    this.state = {
      todoList: ... (see above)
    }

    this.renderTodoItems = this.renderTodoItems.bind(this)
    this.handleAdding = this.handleAdding.bind(this)
    this.handleDelete = this.handleDelete.bind(this)

  }

  renderTodoItems() {
    let list = this.state.todoList
    list = list.map(
      item =>
      <TodoItem key={item.id} item={item} handleDelete={this.handleDelete}/>
      )
    return list
  }

  handleAdding(input) {...}

  handleDelete(item) {...}
  
  render() {
    return (
      <div>
        <AddBar handleAdding={this.handleAdding} />
        {this.renderTodoItems()}
      </div>
    );
  }
}

TodoItem create an item with subitems

class TodoItem extends Component {
    constructor(props) {
        super(props)

        this.renderSubList = this.renderSubList.bind(this)
        this.handleCallback = this.handleCallback.bind(this)

    }

    renderSubList() {
        let subList = this.props.item.subList
        if (subList === undefined)
            return
        subList = subList.map(
            item =>
            <Item key={item.id} item={item} />
            )
          return subList

    }

    handleCallback(item) {
        this.props.handleDelete(item)
    }
  
  render() {
      const {item} = this.props
    return (
      <div>
          <Item key={item.id} item={item} handleCallback={() => this.props.handleDelete(item)} />
          <div className={(item.subList === undefined) ? '' : 'subList'}>
            {this.renderSubList()}
          </div>
      </div>
    );
  }
}

Finally I have Item which displays my data

class Item extends Component {

    render() {
        const {item} = this.props
        const task = item.task
        return (
            <div onClick={() => this.props.handleCallback(this.props.item)} className='item-container'>
                <span>{task}</span>
            </div>
        )
    }
}

I expect to delete the correct task or subtask when Item is clicked.


Solution

  • This code confuses me:

    handleCallback() {
            const handleCallback = this.props.handleCallback
            const item = this.props.item
            console.log(item)
            handleCallback(item)
        }
    
    <div onClick={() => this.handleCallback(item)} className='item-container'>
                    <span>{task}</span>
                </div>
    

    Why don't you change it to this:

    <div onClick={() => this.props.handleCallback(this.props.item)} className='item-container'>
                    <span>{task}</span>
                </div>
    

    The second handleCallback is not needed, it does no additional logic.