React / Redux在状态更新时不触发子渲染

时间:2016-09-13 20:10:26

标签: reactjs redux react-redux

我正在学习React / Redux,并且遇到了困难。在我正在处理的todo应用程序示例中,当添加新的todo时,将执行addTodo操作,并且我可以逐步执行store.dispatch逻辑。失败的是hasStatePropsChanged值被计算为false因此没有子更新。

代码段如下:

import React from 'react';
import { connect } from 'react-redux';
import { store, addTodo, completeTodo, deleteTodo, clearTodo } from './TodoState.jsx';

class AddTodoForm extends React.Component {
    ...
}

class TodoItem extends React.Component {
    ....
}

let TodoList = ({items}) => (
    <ul>
        {items.map((item,index) =>
            <TodoItem key={index} index={index} message={item.message} completed={item.completed}/>
        )}
    </ul>
)

let TodoComponent = ({ items, onAddTodo, onCompleteTodo, onDeleteTodo, onClearTodo }) => /* expand's props */
    (
        <div>
            <h1>Todo</h1>
            <AddTodoForm onAddTodo={onAddTodo} message/>
            <TodoList items={items} onCompleteTodo={onCompleteTodo} onDeleteTodo={onDeleteTodo} onClearTodo={onClearTodo}/>
        </div>
    )

const mapStateToProps = (state) => {
    return {
        items: state.todo.items
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        onAddTodo(message) {
            dispatch(addTodo(message))
        },
        onCompleteTodo(index) {
            dispatch(completeTodo(index))
        },
        onDeleteTodo(index) {
            dispatch(deleteTodo(index))
        },
        onClearTodo(index) {
            dispatch(clearTodo(index))
        }
    }
}

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

AddTodoForm正确调度addTodo动作,问题是TodoList组件即使通过items数组再次渲染也是一个新数组。

更新: 我的减速机确实恢复了新的状态。

这是减速器和动作代码:

import { createStore } from 'redux';
var defaultState = { todo: { items: [] } } 

const ADD_TODO = 1;
const COMPLETE_TODO = 2;
const DELETE_TODO = 3;
const CLEAR_TODO = 4;

const addTodo = (message) => { return {type: ADD_TODO, message: message, completed: false} };
const completeTodo = (index) => { return {type: COMPLETE_TODO, index:index} };
const deleteTodo = (index) => { return {type: DELETE_TODO, index:index} };
const clearTodo = (index) => { return {type: CLEAR_TODO, index:index} };

function todoReducer(state,action) {
    switch(action.type) {
        case ADD_TODO:
            var newState = Object.assign({},state);
            newState.todo.items.push({message:action.message,completed:false});
            return newState;
        case COMPLETE_TODO:
            var newState = Object.assign({},state);
            newState.todo.items[action.index].completed = true;
            return newState;
        case DELETE_TODO:
            var 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;
    }
}

var store = createStore(todoReducer,defaultState);

export { store, addTodo, completeTodo, deleteTodo, clearTodo };

谢谢,

亚伦

1 个答案:

答案 0 :(得分:2)

检查是否将新对象作为reducer中的状态返回。 例如:  return Object.assign ({}, state, {items: [...oldItems, newItem]})

注意[...oldItems, newItem]这将创建新阵列。在您的情况下,Object.assign只执行浅拷贝,实际上项目已更改,但保留相同的引用。看看工作示例:

import React from 'react';                                                                                               
import { render } from 'react-dom';                                                                                      
import { connect, Provider } from 'react-redux';                                                                         

import { createStore } from 'redux';                                                                                     

var defaultState = { todo: { items: [] } }                                                                               

const ADD_TODO = 1;                                                                                                      
const COMPLETE_TODO = 2;                                                                                                 
const DELETE_TODO = 3;                                                                                                   
const CLEAR_TODO = 4;                                                                                                    

const addTodo = (message) => { return {type: ADD_TODO, message: message, completed: false} };                            
const completeTodo = (index) => { return {type: COMPLETE_TODO, index:index} };                                           
const deleteTodo = (index) => { return {type: DELETE_TODO, index:index} };                                               
const clearTodo = (index) => { return {type: CLEAR_TODO, index:index} };                                                 

function todoReducer(state,action) {                                                                                     
    switch(action.type) {                                                                                                
        case ADD_TODO:                                                                                                   
            var newItem = {message:action.message,completed:false};                                                      
            return Object.assign({},state, {todo: {items: [...state.todo.items, newItem]}});                             
        case COMPLETE_TODO:                                                                                              
            var newState = Object.assign({},state);                                                                      
            newState.todo.items[action.index].completed = true;                                                          
            return newState;                                                                                             
        case DELETE_TODO:                                                                                                
            var 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;                                                                                                
    }                                                                                                                    
}                                                                                                                        

var store = createStore(todoReducer,defaultState);                                                                       

class AddTodoForm extends React.Component {                                                                              
    render() {                                                                                                           
        return <button onClick={this.props.onAddTodo}>test</button>                                                      
    }                                                                                                                    
}                                                                                                                        

class TodoItem extends React.Component {                                                                                 
    render() {                                                                                                           
        return <span>item</span>                                                                                         
    }                                                                                                                    
}                                                                                                                        

let TodoList = ({items}) => (                                                                                            
  <ul>                                                                                                                   
      {items.map((item,index) =>                                                                                         
        <TodoItem key={index} index={index} message={item.message} completed={item.completed}/>                          
      )}                                                                                                                 
  </ul>                                                                                                                  
)                                                                                                                        

let TodoComponent = ({ items, onAddTodo, onCompleteTodo, onDeleteTodo, onClearTodo }) => /* expand's props */            
  (                                                                                                                      
    <div>                                                                                                                
        <h1>Todo</h1>                                                                                                    
        <AddTodoForm onAddTodo={onAddTodo} message/>                                                                     
        <TodoList items={items} onCompleteTodo={onCompleteTodo} onDeleteTodo={onDeleteTodo} onClearTodo={onClearTodo}/>  
    </div>                                                                                                               
  )                                                                                                                      

const mapStateToProps = (state) => {                                                                                     
    return {                                                                                                             
        items: state.todo.items                                                                                          
    }                                                                                                                    
}                                                                                                                        

const mapDispatchToProps = (dispatch) => {                                                                               
    return {                                                                                                             
        onAddTodo(message) {                                                                                             
            dispatch(addTodo(message))                                                                                   
        },                                                                                                               
        onCompleteTodo(index) {                                                                                          
            dispatch(completeTodo(index))                                                                                
        },                                                                                                               
        onDeleteTodo(index) {                                                                                            
            dispatch(deleteTodo(index))                                                                                  
        },                                                                                                               
        onClearTodo(index) {                                                                                             
            dispatch(clearTodo(index))                                                                                   
        }                                                                                                                
    }                                                                                                                    
}                                                                                                                        

var Wrapper = connect(mapStateToProps,mapDispatchToProps)(TodoComponent);                                                

render(                                                                                                                  
  <Provider store={store}>                                                                                               
      <Wrapper />                                                                                                        
  </Provider>,                                                                                                           
  document.getElementById('app')                                                                                         
)