scartout
scartout

Reputation: 93

React-Redux - onClick firing on every state change

I want to removing TodoItem by clicking on TodoItem, but React returns "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.". It is returned because my on click function is called on mount, keyup on input or after clicking Add button and I completely don't know why it works in this way.

App.js (component)

class App extends Component {

  render() {
    return (
        <BrowserRouter>
          <div className="App">
            <Header/>
            <div className="container">
                <div className="row">
                    <InputContainer/>
                </div>
                <TodosList todos={this.props.todos}/> {}
            </div>
          </div>
        </BrowserRouter>
    );
  }
}

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

const mapDispatchToProps = {};

export const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);

TodosList.js (component)

import * as React from "react";
import { connect } from "react-redux";
import { TodoItem } from "./TodoItem"
import { handleRemoveTodo } from "./actions"

export class TodosListC extends React.Component{

    todoToTodoItem = todo => {
        return <TodoItem key={todo.id} todo={todo} onClick={this.removeTodo(todo.id)}/>
    };

    render(){
        return(
            <ul className='col-sm-6 col-sm-offset-3'>
                {this.props.todos.map(this.todoToTodoItem)}
            </ul>
        )
    }

    removeTodo = e => {
        this.props.handleRemoveTodo(e);
    }

}

const mapStateToProps = state => {
    return {
        todos: state.todos,
        newInput: state.newInput
    };
};

const mapDispatchToProps = { handleRemoveTodo };

export const TodosList = connect(mapStateToProps, mapDispatchToProps)(TodosListC);

Todos.js (reducer)

export const todos = (state = initState, action) => {
  switch (action.type) {
    case 'HANDLE_SAVE_VALUE':
      return [
        ...state,
        {
            id: action.id,
            name: action.text
        }
      ]
    case 'HANDLE_REMOVE_TODO':
      //firing on input keyup, click submit button and component mount
      console.log('HANDLE_REMOVE_TODO',action.id)
      var newState = state.filter(function(todo) {
          return action.id !== todo.id
      });
      return[
        newState
      ]
    default:
      return state
  }
}

const initState = 
    [{id:99998, name: 'Todo99998'},{id:99999, name:'Todo99999'}]

Link to project zip Link

Thanks in advance for your help.

Upvotes: 1

Views: 1057

Answers (3)

Arulmozhi Manikandan
Arulmozhi Manikandan

Reputation: 336

I have cleaned up the code little bit, there was a misconfiguration of mapToProps and unwanted methods, I have removed those this will remove element on click yet you need to fix few stuff in reducer to remove specific data. Best of luck

////TodoListC.js

import * as React from "react";
import {
  connect
} from "react-redux";
import {
  bindActionCreators
} from "redux";
import {
  TodoItem
} from "./TodoItem"
import {
  handleRemoveTodo
} from "./actions"

export class TodosListC extends React.Component {


    render() {
        return ( <
          ul className = 'col-sm-6 col-sm-offset-3' > {
            this.props.todos.map((todo) => < TodoItem key = {
                todo.id
              }
              id = {
                todo.id
              }
              todo = {
                todo
              }
              handleRemoveTodo = {
                this.props.handleRemoveTodo
              }
              />)} <
              /ul>
            )
          }


        }

        const mapStateToProps = state => {
          return {
            todos: state.todos,
            newInput: state.newInput
          };
        };

        const mapDispatchToProps = dispatch => bindActionCreators({
          handleRemoveTodo
        }, dispatch)

        export const TodosList = connect(mapStateToProps, mapDispatchToProps)(TodosListC);

////TodoItem.js
import * as React from "react";

export const TodoItem = ({ todo, id, handleRemoveTodo}) => {

	return(
		<li key={id} onClick={() => handleRemoveTodo(id)}>{todo.name}</li>
	)

};

Upvotes: 0

James
James

Reputation: 5747

You're executing the function on render here

<TodoItem ... onClick={this.removeTodo(todo.id)}/>

onClick expects a function, but you're calling another function inside of removeTodo and returning nothing, so to fix your code you could just re-write it as something like this

<TodoItem ... onClick={() => this.removeTodo(todo.id)} />

Upvotes: 1

Arulmozhi Manikandan
Arulmozhi Manikandan

Reputation: 336

Update todoToTodoItem method will fix this error :

todoToTodoItem = todo => {
  return <TodoItem key = {
    todo.id
  }
  todo = {
    todo
  }
  onClick = {
    () => this.removeTodo(todo.id)
  }
  />
};

Upvotes: 0

Related Questions