Reputation: 295
Before React Hooks, I had a code like this:
interface State {
f1: number;
f2: number;
}
class OldClassParent extends React.Component<{}, State> {
constructor(props: {}) {
super(props);
this.state = {
f1: 0,
f2: 0
};
}
public render() {
const { f1, f2 } = this.state;
return (
<>
{/* Each pure component re-render only if its value has changed (OK) */}
<MyPureCmp value={f1} name="f1" onChange={this.onChange} />
<MyPureCmp value={f2} name="f2" onChange={this.onChange} />
</>
);
}
private onChange = (name: string, newValue: number) => {
// @ts-ignore
this.setState({
[name]: newValue
});
}
}
Now, I try to have the same behavior with React-hooks. I made the following:
const NewHookParent: React.FC = () => {
const [state, setState] = useState({
f1: 0,
f2: 0
});
// useCallback does not help me here
const onChange = useCallback((name: string, newValue: number) => {
setState({...state, [name]: newValue});
}, [state, setState]);
const { f1, f2 } = state;
return (
<>
{/* Each pure component re-render when any value change
as the onChange handler is never the same (NOT OK) */}
<MyPureCmp value={f1} name="f1" onChange={onChange} />
<MyPureCmp value={f2} name="f2" onChange={onChange} />
</>
);
}
The problem is that I have lost the re-rendering optimization I had before. In this example, for simplicity, I used only two fields, but actually I may have any number of fields (and it can be dynamic, not known at compile time). What should I do ?
Upvotes: 0
Views: 428
Reputation: 191976
The setState
function produced by useState
, accepts an updater function. The updater is called with the current state. Using this allows replaces passing the state to useCallback
, and since the setState
itself won't change, useCallback
will return the same function all the time.
const onChange = useCallback((name: string, newValue: number) => {
setState(state => ({...state, [name]: newValue}));
}, [setState]);
Upvotes: 1