组件(React / Redux)中仅呈现了一部分更新状态

时间:2018-08-04 09:48:37

标签: reactjs redux react-redux

我目前正在与Redux结合使用React,遇到一个奇怪的问题:

如果状态的一个属性被更改,则视图将在组件中更新。如果另一个属性已更改,则不会更新-除非我再次更新第一个属性。我将尝试使其更加清晰:

这是商店界面:

export interface StoreState {
    counter: number;
    rectangles: Rectangle[];
}

这是减速器

export function enthusiasm(state: StoreState, action: EnthusiasmAction): StoreState {
    switch (action.type) {
        case INCREMENT_ENTHUSIASM:
            return { ...state, counter: state.counter + 1 };
        case DECREMENT_ENTHUSIASM:
            return { ...state, counter: Math.max(1, state.counter - 1) };
        case ADD_RECTANGLE:
            let updatedRectangles: Rectangle[] = [];
            if(state.rectangles){
                updatedRectangles = state.rectangles;
            }
            updatedRectangles.unshift(action.payload);
            return {...state, rectangles: updatedRectangles};
        default:
            return state;
    }
}

这是包装组件的容器:

export function mapStateToProps(state: StoreState) {
    return {
        enthusiasmLevel: state.counter,
        rectangles:state.rectangles,
    }
}

export function mapDispatchToProps(dispatch: Dispatch<actions.EnthusiasmAction>) {
    return {
        onIncrement: () => dispatch(actions.incrementEnthusiasm()),
        onDecrement: () => dispatch(actions.decrementEnthusiasm()),
        addRectangle: () => dispatch(actions.addRectangle({posX: Math.floor(Math.random() * 100), posY: Math.floor(Math.random() * 100), id: Math.floor(Math.random() * 1000)}))
    }
}

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

这是我要渲染的组件:

function Hello({ counter = 1, rectangles = [], onIncrement, onDecrement, addRectangle }: Props) {
    console.log(counter, rectangles);
    return (
        <div className="hello">
                <button onClick={onDecrement}>-</button>
                <button onClick={onIncrement}>+</button>
                <button onClick={addRectangle}>+</button>
            <Stage width={window.innerWidth} height={window.innerHeight}>
                <Layer>
                    {
                        rectangles.map((rectangle: Rectangle) => {
                            return <Rect
                                key={rectangle.id}
                                x={rectangle.posX}
                                y={rectangle.posY}
                                width={150}
                                height={150}
                                fill={'green'}
                                draggable
                                onDragStart={handleDragStart}
                                onDragEnd={handleDragEnd}
                            />
                        })
                    }
                </Layer>
            </Stage>
        </div>
    );
}

当我增加计数器时,视图将更新并记录状态。

enter image description here

当我调用addRectangle时,即使状态已更改,视图也不会更新(并且状态未记录在组件中):enter image description here

现在,当我再次增加计数器时,该视图最终将得到更新,并且之前添加的所有矩形都将添加到该视图中(并再次记录状态):

enter image description here

由于我是新来的反应者,因此问题似乎很明显,但是我无法弄清楚我做错了什么。非常感谢您的帮助,谢谢!

1 个答案:

答案 0 :(得分:4)

您正在对state.rectangles进行变异。如果状态不变,则Redux会跳过渲染。您应该改为创建一个新数组。在redux文档中对此进行了说明:Updating nested objects

  

常见错误#1:指向相同对象的新变量

     

定义新变量不会创建新的实际对象-仅它   创建对同一对象的另一个引用。

在这种情况下,我们的问题是updatedReactangles不是新数组。

case ADD_RECTANGLE:
    let updatedRectangles: Rectangle[] = [];
    if(state.rectangles){
        updatedRectangles = state.rectangles;  // still the same array
    }
    updatedRectangles.unshift(action.payload);
    return {...state, rectangles: updatedRectangles};

尝试这样的方法:

case ADD_RECTANGLE:
    let updatedRectangles: Rectangle[] = [];
    if(state.rectangles){
        updatedRectangles = state.rectangles.slice();  // shallow copy
    }
    updatedRectangles.unshift(action.payload);
    return {...state, rectangles: updatedRectangles};

...运算符可使此错误更容易避免。由于不涉及诸如Array.unshift()之类的变异方法,因此我们可以确保我们不会无意间破坏了先前的状态。

case ADD_RECTANGLE:
  return {
    ...state, 
    rectangles: [...state.rectangles, action.payload],
  }

但是通过为reducer提供默认的初始状态参数,确保state.rectangles始终是数组而不是undefined

function reducer(state={ rectangles: [] }, action) {