我正在学习ReactJs,尝试使用useState
和useContext
钩子时遇到了一个问题。似乎仅在存在参考更改而不是值更改的情况下才会进行重新渲染。以下面的代码为例:
https://stackblitz.com/edit/react-ts-4gw3h1
class Counter {
count: number;
constructor(count: number) {
this.count = count;
}
increase() {
this.count += 1;
}
}
export const CountComponent : FC = (props) => {
//const [count, setCount] = useState(0); // work
//const [count, setCount] = useState({0}); // work
const [counter] = useState(new Counter(0)); // not work
const doAdd = () => {
counter.increase();
console.log(counter.count);
}
// this will not actually re-render
return (<div>{counter.count}<button onClick={doAdd}>+</button></div>);
}
单击按钮时,计数增加,但是呈现的值未更新。我在那里错过了什么吗?有没有一种方法可以让组件监视状态对象属性的变化?
编辑
我想我已经将人们与这个问题的意图混淆了。考虑一个更复杂的用例,其中我有一个复杂的状态对象。请用伪代码对付我:
class Cargo {
orders: Map<string, Products[]>;
totalValue: number;
totalNumber: number;
addProduct(product: Product) {
// ignore the null check here.
this.orders.get(product.owner).push(product);
this.totalValue += product.value;
this.totalNumber ++;
}
}
class Product {
owner: string;
name: string;
value: number;
}
const CargoContext = createContext(new Cargo());
const CargoContext = createContext({... new Cargo(), setTotalValue, setTotalNumber});
// lost the OO
const {orders, totalValue, totalNumber, setTotalValue, setTotalNumber} = useContext(CargoContext);
const doAdd = (product: Product) => {
orders.get(product.owner);
setTotalValue(product.value);
setTotalNumber(totalNumber + 1);
}
以这种方式创建上下文时,我将无法实际更新状态。根据我的阅读,使之起作用的唯一方法是解构Cargo对象并传递属性和调度程序。但这会丢失面向对象的Cargo
和addProduct
接口。如何更优雅地处理此案?
答案 0 :(得分:1)
在处理复杂状态时,最好使用useReducer
。跟您的货物示例一样,它会像
interface CargoState {
orders: Map<string, Products[]>;
totalValue: number;
totalNumber: number;
}
const cargoReducer = (state: CargoState, action): CargoState => {
switch (action.type) {
case 'add-product': {
const newOrders = state.orders;
newOrders.get(action.product.name).push(action.product);
return {
...state,
orders: newOrders,
totalValue: state.totalValue + product.value,
totalNumber: state.totalNumber++,
}
}
default: return state;
}
}
const [state, dispatch] = useReducer(cargoReducer, [some initial state here]);
dispatch({ type: 'add-product', product });
可以正确键入操作,因此您不会丢失任何键入内容。