useFormContext changes the value of the input and the state of the request payload sent to API.
import { FC, useEffect, useRef, useState } from "react";
import {
ArrayInput,
BooleanInput,
Create,
NumberInput,
SelectInput,
SimpleFormIterator,
TextInput,
required,
AutocompleteArrayInput,
ImageInput,
useRedirect,
Form,
SimpleForm,
} from "react-admin";
import Box from "@mui/material/Box";
import { baseUrl } from "../dataProvider";
import { useFormContext } from "react-hook-form";
import { useOutsideAlerter } from "../hooks/useOutsideClick";
const initValue = {
index: null,
position: [0, 0],
};
const CreateForm: FC<{ ingredientChoices: { id: string; name: string }[] }> = ({
ingredientChoices,
}) => {
const [availableIngredients, setAvailableIngredients] = useState<
Record<number, string>
>({});
const [nameValue, setNameValue] = useState<{ name: string; index: number }>({
name: "",
index: 0,
});
const [open, setOpen] = useState<{
index: number | null;
position: number[];
}>(initValue);
const { setValue } = useFormContext();
const outside = useRef(null);
useOutsideAlerter(outside, () => setOpen(initValue));
const getChoices = () => {
return Object.values(availableIngredients).map((value) => ({
id: value,
name: value,
}));
};
const fetchAttributes = (id: string) => {
fetch(`${baseUrl}/bo/ingredients/${id}`)
.then((res) => res.json())
.then((data) => {
Object.entries(data).forEach(([key, value]) => {
if (
[
"units",
"value",
"kcal",
"protein",
"carbs",
"fat",
"allergies",
].includes(key)
) {
setValue(`ingredients.${nameValue.index}`, { [key]: value });
}
});
});
};
return (
<>
{open.index === nameValue.index && (
<Box
ref={outside}
boxShadow={2}
sx={{
position: "absolute",
maxHeight: "250px",
overflowY: "scroll",
overflowX: "hidden",
top: open.position[1] + 500,
left: open.position[0],
borderRadius: "10px",
backgroundColor: "white",
zIndex: 1000,
}}
>
{ingredientChoices
.filter(({ name }) =>
nameValue.name ? name.includes(nameValue.name) : true,
)
.map(({ id, name }) => (
<Box
p={1}
key={id}
sx={{
cursor: "pointer",
}}
onClick={() => {
setValue(`ingredients.${nameValue.index}`, { name });
setOpen(initValue);
fetchAttributes(id);
}}
>
{name}
</Box>
))}
</Box>
)}
<ArrayInput source="ingredients" validate={[required()]}>
<SimpleFormIterator inline>
<TextInput
onChange={(e) => {
const rect = e.target.getBoundingClientRect();
setAvailableIngredients({
...availableIngredients,
[e.target.id.split(".")[1]]: e.target.value,
});
setNameValue({
name: e.target.value,
index: +e.target.id.split(".")[1],
});
setOpen({
index: +e.target.id.split(".")[1],
position: [rect.left, rect.top],
});
}}
source="name"
validate={[required()]}
helperText={false}
/>
<TextInput
validate={[required()]}
source="units"
helperText={false}
/>
<NumberInput
validate={[required()]}
source="value"
helperText={false}
/>
<NumberInput
validate={[required()]}
source="kcal"
helperText={false}
/>
<NumberInput
validate={[required()]}
source="protein"
helperText={false}
/>
<NumberInput
validate={[required()]}
source="carbs"
helperText={false}
/>
<NumberInput
validate={[required()]}
source="fat"
helperText={false}
/>
<SelectInput choices={getChoices()} source="linkedIngredient" />
<AutocompleteArrayInput
fullWidth
choices={allergenChoices}
source="allergies"
/>
</SimpleFormIterator>
</ArrayInput>
</>
);
};
const RecipeCreate = () => {
const redirect = useRedirect();
const [ingredientChoices, setIngredientChoices] = useState<
{
id: string;
name: string;
}[]
>([]);
useEffect(() => {
fetch(`${baseUrl}/bo/ingredients`)
.then((res) => res.json())
.then((data) => {
setIngredientChoices(data);
});
}, []);
return (
<Create
mutationOptions={{
onSuccess: () => {
redirect("/");
},
}}
>
<SimpleForm>
<CreateForm ingredientChoices={ingredientChoices} />
</SimpleForm>
</Create>
);
};
export default RecipeCreate;
This should basically create a dropdown of all the available ingredients in the databse and when one is clicked, the data in the form should prefill... But now I'm getting this weird case when I submit the prefilled form.
Screenshot from backoffice create form
I tried changing out the form provider library, none of them worked... Also tried moving the form itself out into a separate component, to see if the problem was the Form component