A volte si vorrebbe poter sfruttare parti comuni di un form usato per gestire più varianti della stessa entità o diverse entità e avere quindi delle parti dinamiche o condizionabili.
Immaginiamo per esempio un form per l'invio di ticket di due diverse nature ma gestiti dallo stesso endpoint; un esempio di modello potrebbe essere
type Ticket = {
type: "INFO",
request: string,
context: string,
} | {
type: "ISSUE",
request: string,
description: string,
part?: { name: string, code: string },
priority: "H" | "M" | "L",
}
Il componente che gestisce la persistenza con l'API potrebbe avere questa struttura
const handleSubmit = useCallback((values: Ticket) => {
console.log("API post request", values);
}, []);
const handleValidation = useCallback((values: Ticket) => {
console.log("values on validation", values);
// no error returned
return {}
}, []);
const firstValues = useMemo<Ticket>(() => {
let result: Ticket = {
type: "INFO",
request: "I'd like request info about...",
context: "PRODUCTS"
};
if (props.ticketNature === "ISSUE") {
result = {
type: "ISSUE",
request: "I've a problem with...",
description: "",
priority: "M",
part: { code: "", name: "" },
}
}
return result;
}, [props]);
<Formik
validate={handleValidation}
onSubmit={handleSubmit}
initialValues={firstValues}
>
{(formProps: FormikProps<any>) => (
<>
<p>{formProps.values.type === "INFO" ? "Info" : "Issue"} ticket</p>
<Form>
{props.ticketNature === "INFO" ? <InfoTicketInputs /> : <IssueTicketInputs />}
<button type="submit" >Submit</button>
</Form>
</>
)}
</Formik>
Il componente Formik, che fa da wrapper al form, ci permette di abbinare le funzioni di gestione submit e validazione e, con il metodo delle render prop, ci mette a disposizione proprietà utili per monitorare lo stato come values
.
Il corpo del form invece può essere condizionabile e attraverso il contesto creato da Formik si possono recuperare valori e avere funzioni helper nei singoli componenti specifici.
Di seguito i componenti presentati sono privi di layout e label per focalizzarsi meglio sullo scopo del post.
Il primo componente per gestire i ticket di tipo INFO potrebbe essere semplicemente
export function InfoTicketInputs() {
return (
<>
<Field type="text" name="request" />
<Field as="select" name="context">
<option value="PRODUCTS">Products</option>
<option value="ACCESSORIES">Accessories</option>
<option value="CATALOGS">Catalogs</option>
</Field>
</>
)
}
Sfruttiamo il componente Field di Formik che ci permette facilmente di mappare la prop da gestire tramite name
e tipizzare il tipo di componente con type
.
Il componente per gestire ticket di tipo ISSUE invece potrebbe avere una gestione dei campi dipendente da altro (l'esempio è assolutamente triviale)
export function IssueTicketInputs() {
const { values, setFieldValue, getFieldProps } = useFormikContext<Ticket>();
const handlePriority = useCallback((priorityValue: string) => {
setFieldValue("priority", priorityValue);
if (priorityValue === "H") {
setFieldValue("description", `HIGH PRIORITY: ${getFieldProps("description").value}`)
} else {
setFieldValue("description", (getFieldProps("description").value as string).replace("HIGH PRIORITY: ", ""));
}
}, [values]);
return (
<>
<Field type="text" name="request" />
<Field type="text" name="description" />
<Field type="text" name="part.name" />
<Field type="text" name="part.code" />
<Field as="select" name="priority" onChange={(e: any) => handlePriority(e.target.value)}>
<option value="H">High</option>
<option value="M">Medium</option>
<option value="L">Low</option>
</Field>
</>
)
}
Come si può notare l'hook useFormikContext
permette di recuperare oltre ai valori attuali dello stato del form values
anche di avere a disposizione degli helper, setFieldValue
getFieldProps
per recuperare o impostare i campi in modo programmatico.
Altra cosa da notare è che a name possiamo passare un path di prop per gestire valori annidati come nel caso di part.
Questo è solo un piccolo esempio di utilizzo di Formik ma utile a far intuire che nei casi in cui si devono gestire parecchi form torna molto utile sfruttare questa libreria.
E voi che ne pensate?
Potete trovare una demo in questo repository
Top comments (0)