하이어+플러스! 직원을 위해 제가 구축한 방법은 다음과 같습니다(UI - Auth).
63164 단어 tutorialprogrammingreactwebdev
개요: 인증과 관련된 모든 보기 및 기능, 호출된 모든 기능은 authSlice 감속기에서 가져옵니다.
인증 경로 페이지
인사이드
routes > auth > auth-page.tsx
Outlet은 auth/employees
경로 내에 중첩된 모든 항목을 렌더링합니다.import { Outlet } from 'react-router';
const AuthPage = () => {
return <Outlet />;
};
export default AuthPage;
인증 구성요소
로그인
인사이드
components > sign-in > sign-in.component.tsx
나는 ChangeEvent
, FormEvent
typescript로 내 기능을 정의하기 위해 반응에서 유형 인터페이스를 가져 왔습니다. defaultFormFields
는 양식 필드의 기본 상태입니다.import { ChangeEvent, FormEvent, useState } from 'react';
import BeatLoader from 'react-spinners/BeatLoader';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
import { signInWithGoogle, signInWithEmailAndPassword } from '../../app/features/auth/authSlice';
import { resetError } from '../../app/features/auth/authSlice';
import { useNavigate } from 'react-router-dom';
const defaultFormFields = {
email: '',
password: '',
};
기능
맨 위에:
signInError
상태에서 isLoading
및 auth
소품으로 가능한 로그인 오류 또는 로드 상태를 렌더링합니다. navigate
에서 react-router-dom
func 를 사용하여 성공 시 새 경로로 이동하겠습니다. formFields
및 setFormFields
로 양식 상태를 처리합니다.맨 아래:
handleChange
- 양식 필드 변경 처리resetFormFields
- 제출 후 양식 재설정handleSubmit
- 양식 데이터를 redux 작업으로 보내고, 양식을 재설정하고, 기본 앱으로 리디렉션합니다.signInGooglePopup
- Google을 사용하여 로그인하기 위해 redux 작업을 호출하고, 양식을 재설정하고, 기본 앱으로 리디렉션합니다.const SignIn = () => {
const { signInError, isLoading } = useAppSelector((state) => state.auth);
const navigate = useNavigate();
const dispatch = useAppDispatch();
const [formFields, setFormFields] = useState(defaultFormFields);
const { email, password } = formFields;
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormFields({ ...formFields, [name]: value });
};
const resetFormFields = () => {
setFormFields(defaultFormFields);
};
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
dispatch(signInWithEmailAndPassword(formFields))
.unwrap()
.then(() => {
resetFormFields();
navigate('/app');
})
.catch((error) => {
resetFormFields();
});
};
const signInGooglePopup = async () => {
dispatch(signInWithGoogle())
.unwrap()
.then(() => {
resetFormFields();
navigate('/app');
})
.catch((error) => {
dispatch(resetError());
});
};
return (// removed for simplicity);
};
export default SignIn;
UI
const SignIn = () => {
// removed for simplicity
return (
<div className="items-center px-5 mt-10">
<div className="flex flex-col w-full max-w-md p-6 mx-auto my-6 transition duration-500 ease-in-out transform rounded-lg md:mt-0 secondary-bg-color">
<div>
<div className="mb-8 mt-4">
<h1 className="text-2xl lg:text-3xl text-center">
Already have an account?
</h1>
<p className="text-center font-normal my-2 font-color">
Sign in with your email and password
</p>
</div>
{/* Error Handling */}
{signInError && (
<div className="text-center text-red-600 mb-5 text-lg">
{signInError}
</div>
)}
<div>
{/* Form Starts */}
<form className="space-y-6" onSubmit={handleSubmit}>
{/* Email Field */}
<div>
<label
htmlFor="email"
className="block text-sm font-medium font-color"
>
{' '}
Email address{' '}
</label>
<div className="mt-2">
<input
id="email"
onChange={handleChange}
value={email}
name="email"
type="email"
required
placeholder="e.g [email protected]"
className="font-color primary-bg-color block w-full px-5 py-3 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-500"
/>
</div>
</div>
{/* Password Field */}
<div className="space-y-1">
<label
htmlFor="pass"
className="block text-sm font-medium font-color mb-2"
>
{' '}
Password{' '}
</label>
<div>
<input
id="password"
minLength={6}
onChange={handleChange}
value={password}
name="password"
type="password"
data-testid="password"
required
placeholder="********"
className="font-color primary-bg-color block w-full px-5 py-3 text-base text-neutral-600 placeholder-gray-500 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-bg-indigo-700 focus:ring-offset-2 focus:ring-offset-gray-300"
/>
</div>
</div>
{/* Submit Button */}
<div>
<button
type="submit"
className="flex items-center justify-center w-full px-10 py-4 text-base font-bold text-center text-white transition duration-500 ease-in-out transform rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 bg-indigo-700"
>
{isLoading ? (
<div className="text-center z-index">
<BeatLoader color={'white'} loading={true} />
</div>
) : (
<p>Sign in</p>
)}
</button>
</div>
</form>
{/* Google Sign-in */}
<div className="relative my-4">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-500"></div>
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 text-neutral-800 bg-white">
Or continue with
</span>
</div>
</div>
<div>
<button
onClick={signInGooglePopup}
className="w-full items-center block px-10 py-3.5 text-base font-medium text-center text-blue-600 transition duration-500 ease-in-out transform border-2 border-gray-300 shadow-md rounded-xl focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"
>
<div className="flex items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
width="24px"
height="24px"
>
<path
fill="#fbc02d"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12 s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20 s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
/>
<path
fill="#e53935"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039 l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
/>
<path
fill="#4caf50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36 c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
/>
<path
fill="#1565c0"
d="M43.611,20.083L43.595,20L42,20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571 c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
/>
</svg>
<span className="ml-4 text-white"> Log in with Google</span>
</div>
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default SignIn;
스크린샷
가입하기
인사이드
components > sign-up > sign-up.component.tsx
defaultFormFields
는 양식 필드의 기본 상태입니다.import { ChangeEvent, FormEvent, useState } from 'react';
import { useNavigate } from 'react-router';
import { BeatLoader } from 'react-spinners';
import { signUpUserEmailAndPassword, setSignupError } from '../../app/features/auth/authSlice';
import { useAppDispatch, useAppSelector } from '../../app/hooks';
const defaultFormFields = {
displayName: '',
email: '',
password: '',
confirmPassword: '',
};
기능
대부분의 로그인 양식과 동일한 작업을 수행하고, 양식 변경 및 제출을 처리하고, 기본 앱으로 리디렉션합니다. 그러나 암호가 일치하지 않으면
setSignupError
오류가 발송됩니다.const Signup = () => {
const dispatch = useAppDispatch();
const { isLoading, signUpError } = useAppSelector((state) => state.auth);
const navigate = useNavigate();
const [formFields, setFormFields] = useState(defaultFormFields);
const { email, password, displayName, confirmPassword } = formFields;
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
setFormFields({ ...formFields, [name]: value });
};
const resetFormFields = () => {
setFormFields(defaultFormFields);
};
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (password !== confirmPassword) {
dispatch(setSignupError('Passwords must match'));
return;
}
dispatch(
signUpUserEmailAndPassword({
email,
password,
displayName,
})
)
.unwrap()
.then(() => {
resetFormFields();
navigate('/app');
})
.catch((error) => {
resetFormFields();
});
};
return ({/* removed for simplicity */})
}
UI
const Signup = () => {
{/* removed for simplicity */}
return (
<div className="items-center px-5 mt-5">
<div className="flex flex-col w-full max-w-md p-6 mx-auto transition duration-500 ease-in-out transform rounded-lg md:mt-0 secondary-bg-color">
<div>
<div className="mb-8 mt-4">
<h1 className="text-2xl lg:text-3xl text-center">
Dont have an account?
</h1>
<p className="text-center font-normal my-2 font-color">
Sign up with your email and password
</p>
</div>
{signUpError && (
<div className="text-center text-red-600 mb-5 text-lg">
{signUpError}
</div>
)}
<div>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label
htmlFor="name"
className="block text-sm font-medium font-color"
>
Name
</label>
<div className="mt-2">
<input
value={displayName}
onChange={handleChange}
id="name"
name="displayName"
type="text"
autoComplete="current-name"
required
placeholder="Enter your name"
className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-400 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
/>
</div>
</div>
<div>
<label
htmlFor="email"
className="block text-sm font-medium font-color"
>
Email address
</label>
<div className="mt-2">
<input
value={email}
onChange={handleChange}
id="email"
name="email"
type="email"
autoComplete="current-email"
required
placeholder="Enter your email"
className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-400 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
/>
</div>
</div>
<div className="space-y-1">
<label
htmlFor="password"
className="block text-sm font-medium font-color"
>
Password
</label>
<div className="mt-2">
<input
id="password"
name="password"
type="password"
onChange={handleChange}
value={password}
minLength={6}
required
data-testid="pass"
placeholder="********"
className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-300 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
/>
</div>
</div>
<div className="space-y-1">
<label
htmlFor="confirmPassword"
className="block text-sm font-medium font-color"
>
Confirm Password
</label>
<div className="mt-1">
<input
id="confirmPassword"
name="confirmPassword"
type="password"
onChange={handleChange}
value={confirmPassword}
data-testid="confirmPass"
minLength={6}
required
placeholder="********"
className="primary-bg-color block w-full px-5 py-3 text-base text-neutral-400 placeholder-gray-300 transition duration-500 ease-in-out transform border border-transparent rounded-lg bg-gray-100 focus:outline-none focus:border-transparent focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-300"
/>
</div>
</div>
<div>
<button
type="submit"
className="bg-indigo-700 flex items-center justify-center w-full px-10 py-4 text-base font-medium text-center text-white transition duration-500 ease-in-out transform rounded-xl hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
{isLoading ? (
<div className="text-center z-index">
<BeatLoader color={'white'} loading={true} />
</div>
) : (
<p>Sign up</p>
)}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default Signup;
스크린샷
프로젝트의 UI/인증 부분은 여기까지입니다. 계속 지켜봐 주세요!
Reference
이 문제에 관하여(하이어+플러스! 직원을 위해 제가 구축한 방법은 다음과 같습니다(UI - Auth).), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ajeasmith/hiring-platform-app-hireplus-heres-how-i-built-it-ui-auth-g8e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)