Closed7
よくある Frontend Code(悪い実践のみ)
TypeScript Partial 🙅
type Person = {
surname: string;
middleName?: string;
givenName: string;
};
type PartialPerson = Partial<Person>;
Partial<T>
はオープンソースライブラリでよく見かけますが、実際のアプリケーション開発で使ったことがありません。
Partial
はすべてのプロパティを一括でオプショナルにするため、any
とほぼ変わらず、せっかく定義した型の意味が薄れてしまいます。
Action:
自分の関わるプロジェクトに Partial
が使われていないか確認し、できれば削除することをおすすめします。
refs:
Prop Drilling 🙅
const { control, register, setValue, formState: { errors } } = useForm()
// 第一層
<TopComponent
control={control}
register={register}
setValue={setValue}
errors={errors}
onSubmit={onSubmit}
...
/>
// 第二層
<ComplexComponent
control={control}
register={register}
setValue={setValue}
errors={errors}
...
/>
// 第三層
<RadioField
control={control}
register={register}
error={errors[names.city]}
name={names.city}
required={required}
...
/>
// 第四層
<Controller<T>
control={control}
name={name}
render={({ field }) => (
<Radio prefix={name} items={items} defaultValue={field.value} onValueChange={field.onChange} />
)}
/>
複雑になるにつれて、propsの受け渡しの階層が深くなって、本当に面倒だ。
正解: React Context
import { FormProvider, useForm, useFormContext } from "react-hook-form"
export const App = () => {
const methods = useForm()
return (
<FormProvider {...methods}>
<form>
// 第一層
<TopComponent />
</form>
</FormProvider>
)
}
// 第二層
<ComplexComponent />
// 第三層
<RadioField />
// 第四層
export const DeepNest = () => (
const { control } = useFormContext()
return (
<Controller<T>
control={control}
name={name}
render={({ field }) => (
<Radio prefix={name} items={items} defaultValue={field.value} onValueChange={field.onChange} />
)}
/>
)
)
refs:
React state で search params を二次処理する ❌
export const Page = () => {
const [searchParams, setSearchParams] = useSearchParams()
// 二重管理
const [params, setParams] = useState<Params>({
likeSearchText: searchParams.get('likeSearchText') ?? undefined,
status: (searchParams.get('status') as StatusValue) ?? undefined,
})
// 二重管理
const [page, setPage] = useState<number>(searchParams.get('page') ? Number(searchParams.get('page')) : 1)
const [per, setPer] = useState<number>(searchParams.get('per') ? Number(searchParams.get('per')) : 10)
const handleOnChangePer = (v: string) => {
setPage(1) // 二重管理
setPer(Number(v)) // 二重管理
setSearchParams(...)
}
const handleChangePage = (page: number) => {
setPage(page) // 二重管理
setSearchParams(...)
}
const onSearch = (v) => {
setParams(...) // 二重管理
setSearchParams(...)
}
おすすめ
const [searchParams, setSearchParams] = useSearchParams()
const page = searchParams.get('page') ? Number(searchParams.get('page')) : 1
const per = searchParams.get('per') ? Number(searchParams.get('per')) : 20
const likeSearchText = searchParams.get('likeSearchText') ?? undefined
const status = (searchParams.get('campaignStatus') as StatusValue) ?? undefined
const handleOnChangePer = (v: string) => {
setSearchParams(...)
}
const handleChangePage = (page: number) => {
setSearchParams(...)
}
const onSearch = (v) => {
setSearchParams(...)
}
余計な useEffect ❌
export const Page: FC<Props> = ({ header, isActive }) => {
const [value, setValue] = useState<string>('')
useEffect(() => {
setValue(isActive ? header : '')
}, [isActive, header])
正解
export const Page: FC<Props> = ({ header, isActive }) => {
const [value, setValue] = useState<string>(isActive ? header : '')
余計な useMemo ❌
const {
fieldState: { error: fromError },
} = useController({
control,
name: from.name,
})
const fromErrorMessage = useMemo(() => {
return fromError && 'message' in fromError ? fromError.message : undefined
}, [fromError])
const {
fieldState: { error: toError },
} = useController({
control,
name: to.name,
})
const toErrorMessage = useMemo(() => {
return toError && 'message' in toError ? toError.message : undefined
}, [toError])
return (<>
<FormError errors={{ message: fromErrorMessage ?? '' }} />
<FormError errors={{ message: toErrorMessage ?? '' }} />
</>)
正解
const { errors } = useFormState({ control })
return (<>
<FormError errors={{ message: errors.from.message ?? '' }} />
<FormError errors={{ message: errors.to.message ?? '' }} />
</>)
TypeScript 型定義の箇所に class を使ってる ❌
class IntegerItem { }
class StringItem { }
class DateItem { }
type Item = IntegerItem | DateItem | StringItem
class Account { }
function printAccountSummary(account: Account) { }
おすすめ:データはデータ、型は型
type IntegerItem
type StringItem
type DateItem
type Item = IntegerItem | DateItem | StringItem
interface Account { }
function printAccountSummary(account: Account) { }
TypeScript 配列専用の型を定義する 🙅
type SelectOption = { label: string; value: string }
export type SelectOptions = SelectOption[]
無用
export class ProductIdAndNameModel extends IdAndNameModel<
'ProductIdAndNameModel',
'ProductId',
ProductId
> {}
export class ProductIdAndNameModels extends IdAndNameModels<
'ProductIdAndNameModels',
'ProductId',
'ProductIdAndNameModel',
ProductId,
ProductIdAndNameModel
> {
instantiate = (values: ProductIdAndNameModel[]): ProductIdAndNameModels => new ProductIdAndNameModels(values)
}
👆 ProductIdAndNameModels
は複雑な定義されたが、実は ProductIdAndNameModel[]
このスクラップは25日前にクローズされました