1

I've asked a similar-ish question here before, however my code has changed quite a bit and I can not figure this out. I am certain it's an issue with what I am passing to my action/reducer. I would seriously appreciate it if someone could explain what I am doing wrong here. I really want to get this, just having a hard time with it.

actions.js

import { ADD_TODO, REMOVE_TODO } from '../constants/action-types';

export const addTodo = (todo) => (
  {
    type: ADD_TODO,
    payload: todo
  }
);

export const removeTodo = (id) => (
  {
    type: REMOVE_TODO,
    payload: id
  }
)

reducers.js

import { ADD_TODO, REMOVE_TODO, ADD_OPTIONS } from '../constants/action-types'; 
import uuidv1 from 'uuid';

const initialState = {
  todos: []
};

const rootReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
    return {
        ...state,
        todos: [...state.todos,
          {
            title: action.payload.inputValue,
            id: uuidv1(),
            createdAt: Date(),
            priority: '',
            deadline: '',
            isClicked: false
          }]
    }

    case REMOVE_TODO:
    return {
      ...state,
      todos: [...state.todos.filter(todo => todo.id  !== action.payload)]
    }

    case ADD_OPTIONS:
    return {
      ...state,
      todos: [...state.todos,
      {
        isClicked: false
      }]
    }

    default:
      return state;
  }
}

export default rootReducer;

TodoList.js

import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { removeTodo } from '../actions';
import { connect } from 'react-redux';

const mapDispatchToProps = dispatch => {
  return {
      removeTodo: id => dispatch(removeTodo(id))
    };
  };

const mapStateToProps = state => {
  return {todos: [...state.todos]};
};

class List extends Component {
  render() {
    const mappedTodos = this.props.todos.map((todo, index) => (
      <TodoItem 
      title={todo.title}
      key={index}
      removeTodo={this.props.removeTodo}
      />
    ));
    return (
      mappedTodos
    );
  }
}

const TodoList = connect(mapStateToProps, mapDispatchToProps) (List)
export default TodoList; 

TodoItem.js

import React, { Component } from 'react';
import uuid from 'uuid';
import '../../css/Todo.css';



class TodoItem extends Component {
  render() {
    const todoId = uuid();
    return (
      <div id={todoId}>
        {this.props.title}
        <button onClick={this.props.removeTodo}>X</button>
      </div>
    );
  }
}

export default TodoItem;

2 Answers2

1

You need to wrap your remove handler in an expression that can be evaluated at "click time" and use the todo id from the closure:

class TodoItem extends Component {
  render() {
    const todoId = uuid();
    return (
      <div id={todoId}>
        {this.props.title}
        <button onClick={this.props.removeTodo}>X</button>
      </div>
    );
  }
}

Should be something like...

class TodoItem extends Component {
  render() {
    const todoId = uuid();
    return (
      <div id={todoId}>
        {this.props.title}
        <button onClick={() => this.props.removeTodo(todoId)}>X</button>
      </div>
    );
  }
}
The Dembinski
  • 1,469
  • 1
  • 15
  • 24
  • Ok can we discuss this a little more maybe tomorrow? I have to get to work now, but I REALLY would like to understand this better. I've read docs and stuff but for some reason I just can't wrap my head around this and it's kinda driving me crazy lol – Cole Gonzales Dec 12 '18 at 21:18
  • @ColeGonzales This is described exactly in [Handling Events](https://reactjs.org/docs/handling-events.html). DOM events such as those coming from `onClick`, `onSubmit`, etc passes an event ([SyntheticEvent](https://reactjs.org/docs/events.html)) to specified handler. So you either need to use an arrow function as the answer describes `onClick` or created an intermediary handler on `TodoItem` thats triggered on click and explicitly calls `removeTodo()` prop function with the `id` passed to it. – Alexander Staroselsky Dec 12 '18 at 21:36
0

Along the lines of what @The Dembinski was saying, it works when I change my TodoList component to look like this:

import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { removeTodo } from '../actions';
import { connect } from 'react-redux';

const mapDispatchToProps = dispatch => {
  return {
      removeTodo: id => dispatch(removeTodo(id))
    };
  };

const mapStateToProps = state => {
  return {todos: [...state.todos]};
};

class List extends Component {
  render() {
    const mappedTodos = this.props.todos.map((todo, index) => (
      <TodoItem 
      title={todo.title}
      key={index}
      removeTodo={() => this.props.removeTodo(todo.id)}
      />
    ));
    return (
      mappedTodos
    );
  }
}

const TodoList = connect(mapStateToProps, mapDispatchToProps) (List)
export default TodoList; 

Changing my removeTodo prop in the map here DID fix the issue and now deletes properly. However, if anyone could help me understand this better either by further discussion, or just by pointing my in the right direction as to what I should be researching. Would be greatly appreciated. I'm not after answers, I'm after learning.