Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
]
},
"dependencies": {
"@tanstack/react-form": "^1.27.0",
"@tanstack/react-query": "^5.90.3",
"@tanstack/react-query-devtools": "^5.90.2",
"axios": "^1.13.2",
Expand Down
93 changes: 86 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/components/pages/login/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { LoginForm } from './login-form';
111 changes: 111 additions & 0 deletions src/components/pages/login/login-form/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
'use client';

import { useForm } from '@tanstack/react-form';

import { FormInput } from '@/components/shared';
import { Button } from '@/components/ui';
import { loginSchema } from '@/lib/schema/auth';

const getHintMessage = (errors: unknown[], isTouched: boolean, submissionAttempts: number) => {
const firstError = errors[0] as { message?: string } | undefined;
const showError = isTouched || submissionAttempts > 0;

return showError ? firstError?.message : undefined;
};

export const LoginForm = () => {
const form = useForm({
defaultValues: {
email: '',
password: '',
},
validators: {
onSubmit: loginSchema,
onChange: loginSchema,
},
onSubmit: async ({ value }) => {
// API 호출
alert('login:' + value.email);
},
});

return (
<form
className='flex-col-center w-full gap-8'
onSubmit={(e) => {
e.preventDefault();
void form.handleSubmit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 void라는게 있는지 몰랐네요 좋은 것 같습니다

}}
>
<div className='flex-col-center w-full gap-4'>
<form.Field name='email'>
{(field) => {
const {
meta: { errors, isTouched },
} = field.state;
const hintMessage = getHintMessage(errors, isTouched, form.state.submissionAttempts);
Comment on lines +43 to +46
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getHintMessage가 에러메시지를 처리하는 유틸함수니까, 더 간단하게 이렇게 처리할 수 있을 것 같아요

Suggested change
const {
meta: { errors, isTouched },
} = field.state;
const hintMessage = getHintMessage(errors, isTouched, form.state.submissionAttempts);
const hintMessage = getHintMessage(field);

그러면 아래처럼 isTouched랑 errors를 전부 field에서 뽑을 수 있어서 JSX 리턴문을 좀 더 간소화 시킬 수 있게 돼요!

const getHintMessage = (field: AnyFieldAPI) => {
  const errors = field.state.meta.errors
  ...
};


return (
<FormInput
hintMessage={hintMessage}
inputProps={{
type: 'email',
autoComplete: 'email',
placeholder: '이메일을 입력해주세요',
value: field.state.value,
onChange: (e) => field.handleChange(e.target.value),
}}
labelName='이메일'
/>
);
}}
</form.Field>

<form.Field name='password'>
{(field) => {
const {
meta: { errors, isTouched },
} = field.state;
const hintMessage = getHintMessage(errors, isTouched, form.state.submissionAttempts);

return (
<FormInput
hintMessage={hintMessage}
inputProps={{
type: 'password',
placeholder: '비밀번호를 입력해주세요',
value: field.state.value,
onChange: (e) => field.handleChange(e.target.value),
}}
labelName='비밀번호'
/>
);
}}
</form.Field>
</div>

<form.Subscribe
selector={(state) => ({
canSubmit: state.canSubmit,
isSubmitting: state.isSubmitting,
})}
>
{({ canSubmit, isSubmitting }) => {
const disabled = !canSubmit || isSubmitting;

return (
<Button
className='border-none'
disabled={disabled}
size='md'
type='submit'
variant='primary'
>
로그인하기
</Button>
);
}}
</form.Subscribe>
</form>
);
};
1 change: 1 addition & 0 deletions src/components/pages/signup/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SignupForm } from './signup-form';
Loading
Loading