简体   繁体   中英

Redux Component will not update on store change

I'm trying to get to grips with Redux + React - I have hooked up the relevant bits of Redux with connect() for a small todo app but I cannot for the life of me get the component to update and show the reflected store changes. The store state does update however the component will not. Here are the relevant bits in my code:

actionTypes.js

export const ADD_TODO = "ADD_TODO";
export const DELETE_TODO = "DELETE_TODO";
export const CLEAR_TODO = "CLEAR_TODO";
export const COMPLETE_TODO = "COMPLETE_TODO";

reducers.js

import {ADD_TODO, COMPLETE_TODO, DELETE_TODO, CLEAR_TODO} from '../actions/actionTypes';
const todoApp = (state, action) => {
    let updatedState;
    switch (action.type) {
        case ADD_TODO:
            updatedState = Object.assign({}, state);
            updatedState.todo.items.push({
                text: action.text,
                completed: false
            });
            return updatedState;
        case COMPLETE_TODO:
            updatedState = Object.assign({}, state);
            updatedState.todo.items[action.index].completed = true;
            return updatedState;
        case DELETE_TODO:
            const items = [].concat(state.todo.items);
            items.splice(action.index, 1);
            return Object.assign({}, state, {
                todo: {
                    items: items
                }
            });
        case CLEAR_TODO:
            return Object.assign({}, state, {
                todo: {
                    items: []
                }
            });
        default:
            return state;
    }
};

export default todoApp;

actions.js

import {ADD_TODO, COMPLETE_TODO, DELETE_TODO, CLEAR_TODO} from './actionTypes.js';
export const addTodoCreator = (text) => {
    return {
        type: ADD_TODO,
        text: text,
        completed: false
    }
};

export const completeTodo = (index) => {
    return {
        type: COMPLETE_TODO,
        index: index
    }
};

export const deleteTodo = (index) => {
    return {
        type: DELETE_TODO,
        index: index
    }
};

export const clearTodo = (index) => {
    return {
        type: CLEAR_TODO,
        index: index
    }
};

AddTodoContainer.js

import { connect } from 'react-redux';
import TodoList from '../components/TodoList';
const mapStateToProps = (state, ownProps) => {
    return {
        todo: state.todo
    }

};

export default connect(mapStateToProps)(TodoList);

TodoListContainer.js

import { connect } from 'react-redux';
import {addTodoCreator} from '../actions/actions';
import AddTodo from '../components/AddTodo';
const mapStateToProps = (state) => {
    console.log(state);
    return {
        todo: state.todo
    }
};

const mapDispatchToProps = (dispatch) => {
    return {
        addTodo: (text) => {
            const action = addTodoCreator(text);
            dispatch(action);
        },
    }
};

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

AddTodo.js

import React from 'react'
const handler = (addTodo) => {
    const text = document.getElementById('textInput').value;
    addTodo(text);
};

const AddTodo = ({addTodo}) => {
    return (
        <div>
            <input id="textInput" type="text" className="textInput" />
            <button onClick={(handler).bind(null, addTodo)}>Add</button>
        </div>
    )
}

export default AddTodo

TodoList.js

import React from 'react';
import AddTodoContainer from '../containers/AddTodoContainer';

class TodoList extends React.Component {
    render () {
        console.log(this.props);
        return (
            <div>
                <ul>
                    {this.props.todo.items.map((item) => {
                        return <li>
                            {item.text}
                        </li>
                    })}
                </ul>
                <AddTodoContainer/>
            </div>
        )
    }
}

export default TodoList;

I've tried all of the suggestions under Troubleshooting and as far as I can tell I am not mutating state. The reducer is firing and I can log out the states. The code is stored here under react-fulltodo http://gogs.dev.dylanscott.me/dylanrhysscott/learn-redux

Thanks

Dylan

You're passing todo to your component and while the todo object gets updated the actual todo object in redux state is the same exact object as it was before. So react does not see the object as changed. For example:

const a = { foo: 'bar' };
const b = a;

b.foo = 'I made a change';
console.log(a==b); 
// logs true because a and b are the same object
// This is exactly what's happening in React.
// It sees the object as the same, so it does not update.

You need to clone the todo object so that react sees it as a changed/new object.

In your reducer:

switch (action.type) {
    case ADD_TODO:
        updatedState = Object.assign({}, state);
        // Shallow clone updatedState.todo
        updatedState.todo = Object.assign({}, updatedState.todo);
        updatedState.todo.items.push({
            text: action.text,
            completed: false
        });
        return updatedState;

Meanwhile, if you passed state.todo.items to your component you would not have to clone todo but you would have to clone items . So in the future, if you have a component that directly mapStateToProps with state.todo.items , it will have the same problem because you are not cloning the items array in ADD_TODO like you are in the DELETE_TODO reducer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM