An event handler generator is a function that returns an event handler when invoked with configuration or event-specific arguments. It reduces duplicated handlers that differ only in a value such as a counter delta or form-field key. section 8.7 section 9.2.3
Use the pattern to:
- Create counter handlers from one updater: `update = (delta) => () => setCounter(...)`, then pass `update(1)` or `update(-1)` to buttons. section 8.7
- Update many form fields through one handler: `onChange = (key) => (evt) => ...`, using the key to select the state property. section 9.2.3
- Simplify forms further by reading each input’s `name` from `evt.target`, allowing every field to share one `onChange` handler. section 9.2.3
Address form
Store address fields in one state object and preserve the other values while updating one key: `setData(oldData => ({ ...oldData, [key]: evt.target.value }))`. Each input can receive `onChange={onChange(key)}`; the simpler alternative uses `name="city"` and a shared `onChange`. section 9.2.3
function Counter() {
const [counter, setCounter] = useState(0);
const update = (delta) => () => #1
setCounter((c) => c + delta); #1
return (
<>
<h1>Value: {counter}</h1>
<button onClick={update(1)}>Increment</button> #2
<button onClick={update(-1)}>Decrement</button> #2
</>
);
}
Compare the generated counter handlers with the more verbose inline handlers to see how invocation supplies the event-specific delta. section 8.7
Listing 9.4 Address form
import { useState } from "react";
function Address() {
const [data, setData] = useState({ #1
address1: "", #1
address2: "", #1
zip: "", #1
city: "", #1
state: "", #1
country: "", #1
}); #1
const onChange = (key) => (evt) => { #2
setData((oldData) => #3
({ ...oldData, [key]: evt.target.value }) #3
); #3
};
return (
<form style={{ display: "flex", flexDirection: "column" }}>
<label>
Address line 1:
<input
value={data.address1} #4
onChange={onChange("address1")} #4
/>
</label>
<label>
Address line 2:
<input
value={data.address2} #4
onChange={onChange("address2")} #4
/>
</label>
<label>
Zip:
<input
value={data.zip} #4
onChange={onChange("zip")} #4
/>
</label>
<label>
City:
<input
value={data.city} #4
onChange={onChange("city")} #4
/>
</label>
<label>
State:
<input
value={data.state} #4
onChange={onChange("state")} #4
/>
</label>
<label>
Country:
<input
value={data.country} #4
onChange={onChange("country")} #4
/>
</label>
</form>
);
}
function App() {
return <Address />;
}
export default App;
Inspect a complete address-form implementation using one state object and a handler generator for each field. section 9.2.3
