0
votes

I am studying react-redux. First of all, the action of addTodo looks like this.

let nextTodoId = 0;

export const addTodo = (content) => ({
  type: ADD_TODO,
  payload: {
    id: ++nextTodoId,
    content
  }
});

And the component is here

import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodo } from "../redux/actions";

function AddTodoComponent(props) {
  const [inputValue, setInput] = useState("");
  console.log({ addTodo })

  const handleAddTodo = () => {
    console.log(inputValue)
    props.addTodo(inputValue);
    setInput("");
  };

  return (
    <div>
      <input onChange={(e) => setInput(e.target.value)} />
      <button className="add-todo" onClick={handleAddTodo}>
        Add Todo
      </button>
    </div>
  );
}

export default connect(null, { addTodo })(AddTodoComponent);

I want to use mapStateToProps and mapDispatchToProps but It's not working. I thought It will be work if I use destructing object. But it's not appear in console too. Am I wrong?

import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodo } from "../redux/actions";

function AddTodo({
  allId,
  byId,
  newTodo,
}) {
  const [inputValue, setInput] = useState("");

  const handleAddTodo = () => {
    newTodo(inputValue);
    setInput("");
  };

  return (
    <div>
      <input onChange={(e) => setInput(e.target.value)} />
      <button className="add-todo" onClick={handleAddTodo}>
        Add Todo
      </button>
    </div>
  );
}

const mapStateToProps = state => {
  return {
    allId: state.allId,
    byId: state.byId,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    newTodo: () => dispatch(addTodo()),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);

and this is reducer function.

import { ADD_TODO, TOGGLE_TODO } from "../actionTypes";

const initialState = {
  allIds: [],
  byIds: {}
};

export default function(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO: {
      const { id, content } = action.payload;
      return {
        ...state,
        allIds: [...state.allIds, id],
        byIds: {
          ...state.byIds,
          [id]: {
            content,
            completed: false
          }
        }
      };
    }
    case TOGGLE_TODO: {
      const { id } = action.payload;
      return {
        ...state,
        byIds: {
          ...state.byIds,
          [id]: {
            ...state.byIds[id],
            completed: !state.byIds[id].completed
          }
        }
      };
    }
    default:
      return state;
  }
}

1
Could you include store creation line of code?Hend El-Sahli
+ What did you get when you console.log(props.addTodo ); this?Hend El-Sahli

1 Answers

0
votes

It took me a bit of playing but I found your problem! (3 problems, actually).

  1. Your addTodo function is a function which takes the content of the Todo (a string) and creates the action.
const mapDispatchToProps = (dispatch) => {
  return {
    newTodo: () => dispatch(addTodo())
  };
};

But in your mapDispatchToProps function, you are accepting no arguments and calling addTodo with no arguments. So your Todo gets added, but the content will always be undefined.

Change this to

newTodo: (content) => dispatch(addTodo(content))
  1. In mapStateToProps you've got a misnamed property. You need to change state.byId to state.byIds.

  2. In order to clear the content of the input, you need the value of the input to be controlled by your useState. Add value={inputValue} to the input element.

<input value={inputValue} onChange={(e) => setInput(e.target.value)} />

This is a sidenote, but you might want to think about learning typescript and adding annotations to your projects. There is a bit of a learning curve but the tradeoff is you would be able to catch things like missing arguments and misnamed properties easily. They can be a real head-scratcher otherwise.