Form Mutating and Validation
Answered
Little yellow ant posted this in #help-forum
Little yellow antOP
I'm trying to use server actions to obtain an array of image urls transformed by firebase storage, but the data received by the createImage() action are the files and not the transformed URLs, can anyone help?
'use client'
import { useFormState } from 'react-dom';
import { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';
import { createImage} from '@/actions/images';
import { storage } from '@/lib/firebase';
export default function Form() {
const [imageURLs, setImageURLs] = useState<string[]>([]);
const initialState = { message: null, errors: {} };
const [state, dispatch] = useFormState(createImage, initialState);
const handleImages = async (e) => {
const images = Array.from(e.target.files);
images.forEach((image) => {
const imageId = crypto.randomUUID();
const imageRef = ref(storage, `uploads/images/${image.name}-${imageId}`;
const uploadTask = uploadBytesResumable(imageRef, image.file);
uploadTask.on("state_changed", () => console.log, (error) => console.log(error), async () => {
const downloadURL = await getDownloadURL(uploadTask.snapshot.ref)
setImageURLs(prevURLs => [...prevURLs, downloadURL]);
})
})
}
return (
<form action={dispatch}>
<input
id="imgs"
name="images"
type="file"
accept="image/*"
defaultValue={imageURLs}
onChange={handleImages}
multiple
/>
{state.erros?.images
? (
<div id="images-error" aria-live"polite">
{state.erros.images.map((error: string) =>
<span key={error} className="text-red-500 text-sm">{error}</span>)}
</div>
)
: null
}
<Button type="submit">Submit</Button>
</form>
)import { z } from 'zod';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { sql } from '@vercel/postgres';
export type State = {
errors?: {
images?: string[];
};
message?: string | null;
};
const FormSchema = z.object({
id: z.string(),
images: z.array(z.string()),
createdAt: z.string(),
})
const CreateImage = FormSchema.omit({ id: true, createdAt: true });
export async function createImage(prevState: State, formData: FormData) {
const validatedFields = CreateProperty.safeParse({
images: formData.getAll('images'),
});
if (!validatedFields.success) {
return {
errors: validatedFields.error.flatten().fieldErrors,
message: 'Fill the necessary fields.'
};
}
const { images } = validatedFields.data
const date = new Date().toISOString().split('T')[0];
try {
await sql`
INSERT INTO images (images, created_at) VALUES (${images}, ${date})
ON CONFLICT (id) DO NOTHING;
`
console.log('Image sucessfully created');
} catch (error) {
console.error('Something went wrong', error);
return {
message: 'Database Error: Failed to Create Image.',
};
}
revalidatePath('/dashboard/images');
redirect('/dashboard/images');
}Answered by Ray
{imageURLs.map(url => (
<>
<input key={url} type="hidden" name="images" value={url} />
</>
)