Suomi Suomi - 1 month ago 8
Javascript Question

Why aren't my todo items rendering with Redux?

I'm doing a simple redux / react todo app. I can't get the todo items to show up. I'm able to

console.log
the data, but can't get it to appear. What am I doing wrong?

I separated the files, here is my
app.js
:

import React, { Component } from 'react';
import Todos from './todos';
import TodoList from "./todo_list";

export default class App extends Component {
render() {
return (
<div>
<Todos />
<TodoList/>
</div>
);
}
}


Here is the container
Todos
:

import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addTodo } from '../actions/index';

class Todos extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}

addTodo(e) {
e.preventDefault();
this.props.addTodo(this.state.text);
this.setState({
text: ''
});
}

updateValue(e) {
this.setState({text: e.target.value})
}

render() {
return (
<div>
<form onSubmit={(e) => this.addTodo(e)}>
<input
placeholder="Add Todo"
value={this.state.text}
onChange={(e) => {
this.updateValue(e)
}}
/>
<button type="submit">Add Todo</button>
</form>
</div>
);
}
}


function mapDispatchToProps(dispatch) {
return bindActionCreators({addTodo}, dispatch);
}

export default connect(null, mapDispatchToProps)(Todos);


Here is the
TodoList
:

import React, {Component} from 'react';
import {connect} from 'react-redux';

class TodoList extends Component {

render() {
return (
<ul>
{ this.props.todo.map((tod) => {
return <li key={tod.message}>{ tod.message }</li>
})}
</ul>
);
}
}

function mapStateToProps({ todo }) {
console.log({ todo });
return { todo };
}

export default connect(mapStateToProps)(TodoList);


Reducer:

import { ADD_TODO } from '../actions/types';

export default function(state=[], action) {
switch(action.type) {
case ADD_TODO:
return [ action.payload.message, ...state ]
}
return state;
}


And action

import { ADD_TODO } from './types';
const uid = () => Math.random().toString(34).slice(2);

export function addTodo(message) {
const action = {
id: uid(),
message: message
};
return {
type: ADD_TODO,
payload: action
};
}


This is what I get from the console.log({todo});

enter image description here

Here is my reducers/index:

import { combineReducers } from 'redux';
import TodosReducer from './reducer_addTodo';

const rootReducer = combineReducers({
todo: TodosReducer
});

export default rootReducer;

Answer Source

It's because there's a disconnect between your TodoList and reducer. TodoList, when mapping, expects each todo to have a message prop, but your reducer, when returning next state, only includes the message in the state array, not an object with the message property:

case ADD_TODO:
  return [ action.payload.message, ...state ]

Instead, do not just put the message string in the next state's array, put in the whole object:

case ADD_TODO:
  return [ action.payload, ...state ]

Now every single element in the todo array will be an object and have a message and id property. Also, try using an always unique expression for key -- it really shouldn't be the todo message, nor the id you supplied because it's using Math.random which both have a possibility of keys being the same.