I am learning ReactJs and I ran into some question when trying out the useState and useContext hook. It seems the re-rendering will only happen if there is reference change rather than value change. Take the bellowing code for example:
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>);
}
As you click the button, the count has increased yet the value rendered isn't updated. Am I missing anything there? And is there a way to have the component monitor the state object property change?
EDIT
I think I have confused people with intention of the question. Consider a more complicated use case where I have a complex state object. Pardon me with the psuedocode:
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);
}
When creating a context this way, I won't be able to update the state actually. The only way to make it work from what I read is to deconstruct the Cargo object and passing the property and dispatcher. But that lose the object-oriented Cargo and addProduct interface. How to deal with this case more gracefully?
setCounterfunction. The update function does more than just change the state value. It asks React to compare the old and new values and schedule a render if they're different. The comparison is also a shallow compare, so since your state is an object, the ref needs to change, not just a value inside it.useStatereturns 2 things - its current value and the updater function. You should destructure withconst [counter, setCounter] ...and usesetCounterto update that state, which will trigger a re-renderCounterclass is unnecessary. Just giveuseStatethe initial value of0and you're good to go! (while also following the suggested commented above)