-
I have a create user form using react/tRPC. backend tRPC route: checkIfUserExists:
t.procedure
.input(z.object({ username: z.string() }))
.query(async (opts) => {
const { username } = opts.input
const result = await prisma.user.findUnique({ where: { username } })
return { isDupe: Boolean(result), forValue: username }
}), example of zod Schema (frontend validation) (doesnt matter for question): export const z_user = z.object({
username: z.string()
.nonempty({ message: "This field is required" })
.min(2, { message: "Must be between 2-12 characters" })
.max(12, { message: "Must be between 2-12 characters" }),
password: z.string()
.nonempty({ message: "This field is required" })
.min(2, { message: "Must be between 2-12 characters" })
.max(12, { message: "Must be between 2-12 characters" }),
}).strict() create user form component: import { useEffect, useState } from 'react'
import { RouterInputs, trpc } from '../TrpcProvider'
import { z_user } from '../zodSchemas'
import { TRPCClientError } from '@trpc/client'
interface IProps {
f_getUsers: () => void
}
const userObject: RouterInputs["createUser"] = { username: "", password: "" }
export default function CreateUserForm({ f_getUsers: getUsers }: IProps) {
const [createFormValues, setCreateFormValues] = useState(userObject)
const [createFormErrors, setCreateFormErrors] = useState(userObject)
const [createFormInfo, setCreateFormInfo] = useState("")
const f_createUser = trpc.createUser.useMutation()
const f_checkDupe = trpc.checkIfUserExists.useQuery(
{ username: createFormValues.username },
// problems with enabled here, it's sending fetch even though the validation like .min(2) fails
{ enabled: Boolean(createFormValues.username) }
// probmem with { enabled: false } : it's not sending fetch when it should, and has outdated data from previous render
// ideal solution would be to send variables through the refetch query, but that's not possible with react-query.
// other solutions: creating multiple useEffects or new states which will lead to non reusable sphagetti code. (if I have multiple forms I want to generate that have multiple fields with unique column checks)
)
function onFieldChange(field: keyof typeof userObject, value: string) {
setCreateFormValues(prev => ({ ...prev, [field]: value }))
validateField(field, value)
}
async function validateField(field: keyof typeof userObject, value: string) {
console.log("validating input:", field, "with value:", value)
const zodValidationTest = z_user.shape.password.safeParse(value)
if (!zodValidationTest.success) {
let errors = zodValidationTest.error.formErrors.formErrors
setCreateFormErrors(prev => ({ ...prev, [field]: errors[0] }))
return
}
// passed frontend zod validation
// if username, check if duplicate in database:
if (field === "username") {
let result = await f_checkDupe.refetch()
if (result.isError || !result.isSuccess) {
setCreateFormErrors(prev => ({ ...prev, [field]: "Server error" }))
return
}
if (result.data.isDupe) {
setCreateFormErrors(prev => ({ ...prev, [field]: "Username already exists." }))
return
}
}
console.log("✅ passed validation.")
setCreateFormErrors(prev => ({ ...prev, [field]: "" }))
}
async function createUser() {
// > check if errors exist / validate all fields ...
// > fetch create user ...
}
return (
<form onSubmit={e => { e.preventDefault(); createUser() }} className="form-container">
<div>
Username
<input type="text" value={createFormValues.username} onChange={e => onFieldChange("username", e.target.value)} />
<span>Error:{f_checkDupe.isFetching ? "🔄" : createFormErrors.username}</span>
</div>
<div>
Password
<input type="text" value={createFormValues.password} onChange={e => onFieldChange("password", e.target.value)} />
<span>Error: {createFormErrors.password}</span>
</div>
<button type="submit">CREATE USER</button>
<h4>INFO: {createFormInfo}</h4>
</form>
)
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
use |
Beta Was this translation helpful? Give feedback.
use
queryClient.fetchQuery
to fire a query imperatively