我目前正在与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>
);
}
当我增加计数器时,视图将更新并记录状态。
当我调用addRectangle时,即使状态已更改,视图也不会更新(并且状态未记录在组件中):
现在,当我再次增加计数器时,该视图最终将得到更新,并且之前添加的所有矩形都将添加到该视图中(并再次记录状态):
由于我是新来的反应者,因此问题似乎很明显,但是我无法弄清楚我做错了什么。非常感谢您的帮助,谢谢!
答案 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) {