[React] React hook, component props for refactoring
Idea is put component props inside hook, and return from hook, that's way to keep component clean and simple
Hook:
import { MachineOptions } from "./machine";
import { machine } from "./machine";
import { useMachine } from "@zag-js/react";
import { ComponentProps } from "react";
type LabelProps = ComponentProps<"label"> & { "data-part": "label" };
type InputProps = ComponentProps<"input"> & {
"data-part": string;
};
export function usePinMachine(options: MachineOptions) {
const [state, send] = useMachine(machine(options));
const { value, name } = state.context;
const valueAsString = value.join("");
return {
value,
valueAsString,
getLabelProps(): LabelProps {
return {
"data-part": "label",
onClick() {
send({ type: "LABEL_CLICK" });
},
};
},
getHiddenInput(): InputProps {
return {
"data-part": "hidden-input",
type: "hidden",
name,
value: value.join(""),
};
},
getInputProps({ index }: { index: number }): InputProps {
return {
"data-part": "input",
value: value[index],
maxLength: 2,
onChange(event) {
const { value } = event.target;
send({ type: "INPUT", index, value });
},
onFocus() {
send({ type: "FOCUS", index });
},
onBlur() {
send({ type: "BLUR" });
},
onKeyDown(event) {
const { key } = event;
if (key === "Backspace") {
send({ type: "BACKSPACE", index });
}
},
onPaste(event) {
event.preventDefault();
const value = event.clipboardData.getData("Text").trim();
send({ type: "PASTE", value, index });
},
};
},
};
}
Component:
import "./App.css";
import { usePinMachine } from "./use-pin-machine";
function App() {
const { value, getLabelProps, getHiddenInput, getInputProps } = usePinMachine(
{
numOfFields: 4,
name: "pincode",
onCompleted(value) {
console.log(value);
},
}
);
return (
<div className="App">
<form
onSubmit={(event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
}}
>
<div data-part="container">
<label {...getLabelProps()}>Enter verification</label>
<input {...getHiddenInput()} />
<div data-part="input-group">
{value.map((_, index) => (
<input key={index} {...getInputProps({ index })} />
))}
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default App;