Blitz.jsで使うテンプレコードメモ
Form
app/pages/playgrounds/my_form_page.tsx
import { Head, BlitzPage } from "blitz"
import { Flex, Box, Text } from "@chakra-ui/react"
import Layout from "app/core/layouts/Layout"
import MyForm from "app/playgrounds/components/MyFormComponent"
import {
MyFormContextProvider,
MyFormContextType,
MyFormContext,
} from "app/playgrounds/context/MyFormContext"
import { useContext } from "react"
const MyFormMain = () => {
const context: MyFormContextType = useContext(MyFormContext)
return (
<>
<Head>
<title>Title</title>
</Head>
<Flex bg="white" w="100vw">
<Flex as="header" position="fixed" top={0} width="full" py={4} px={8}>
<MyForm
submitText="検索"
initialValues={{ text: "" }}
onSubmit={(value) => {
alert(Value: ${JSON.stringify(context.text)}
)
}}
></MyForm>
</Flex>
</Flex>
</>
)
}
const MyFormPage: BlitzPage = () => {
return (
<MyFormContextProvider>
<MyFormMain></MyFormMain>
</MyFormContextProvider>
)
}
MyFormPage.authenticate = false
MyFormPage.getLayout = (page) => <Layout>{page}</Layout>
export default MyFormPage
app/playgrounds/components/MyFormComponent.tsx
import {
ReactNode,
forwardRef,
ComponentPropsWithoutRef,
PropsWithoutRef,
useContext,
ChangeEvent,
} from "react"
import {
Form as FinalForm,
FormProps as FinalFormProps,
useField,
UseFieldConfig,
} from "react-final-form"
import { Flex, Button, Input } from "@chakra-ui/react"
import { FormControl, FormLabel } from "@chakra-ui/form-control"
import { validateZodSchema } from "blitz"
import { z } from "zod"
import { MyFormContext, MyFormContextType } from "../context/MyFormContext"
const FormSchema = z.object({
text: z.string(),
})
export interface MyFormProps extends ComponentPropsWithoutRef<typeof Input> {
name: string
label: string
type?: "text" | "number"
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
fieldProps?: UseFieldConfig<string>
}
interface FormProps<S extends z.ZodType<any, any>>
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
children?: ReactNode
submitText?: string
schema?: S
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
}
function Form<S extends z.ZodType<any, any>>({
children,
submitText,
initialValues,
onSubmit,
...props
}: FormProps<S>) {
return (
<FinalForm
initialValues={initialValues}
validate={validateZodSchema(FormSchema)}
onSubmit={onSubmit}
render={({ handleSubmit, submitting, submitError }) => (
<form onSubmit={handleSubmit} className="form" {...props}>
<Flex>
{submitError && (
<div role="alert" style={{ color: "red" }}>
{submitError}
</div>
)}
{children}
{submitText && (
<Button type="submit" disabled={submitting}>
{submitText}
</Button>
)}
</Flex>
</form>
)}
/>
)
}
const MyForm = forwardRef<HTMLInputElement, MyFormProps>(
({ name, label, outerProps, fieldProps, labelProps, ...props }, ref) => {
const context: MyFormContextType = useContext(MyFormContext)
const {
input,
meta: { touched, error, submitError, submitting },
} = useField(name, {
parse: props.type === "number" ? (Number as any) : (v) => (v === "" ? null : v),
...fieldProps,
})
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
return (
<FormControl {...outerProps}>
<FormLabel {...labelProps}>
{label}
<Input
{...input}
disabled={submitting}
{...props}
ref={ref}
value={context.text}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
context.setText(e.target.value)
}}
/>
</FormLabel>
{touched && normalizedError && (
<div role="alert" style={{ color: "red" }}>
{normalizedError}
</div>
)}
</FormControl>
)
}
)
export function MyFormConponent<S extends z.ZodType<any, any>>(props: FormProps<S>) {
return (
<Form<S> {...props} style={{ width: "100%" }}>
<MyForm name="text" label="" placeholder="今日は何があった?" />
</Form>
)
}
export default MyFormConponent
app/playgrounds/context/MyFormContext.tsx
import * as React from "react"
export interface MyFormContextType {
text: string
setText: (t: string) => void
}
export const MyFormContext = React.createContext<MyFormContextType>({
text: "",
setText: (text: string) => {},
})
export const MyFormContextProvider: React.FC = ({ children }) => {
const context: MyFormContextType = React.useContext(MyFormContext)
const [text, setText] = React.useState(context.text)
const newContext: MyFormContextType = {
text,
setText,
}
return <MyFormContext.Provider value={newContext}>{children}</MyFormContext.Provider>
}