인스타그램 클론코딩 10일차 - FE

박병준·2021년 8월 3일
0
post-thumbnail

#6.0 LogIn & SignUp UI

styled-components를 이용해 Login 페이지와 Signup 페이지의 UI를 먼저 만들어 놓는다.

// src/components/auth/FormBox/js
import styled from "styled-components";
import { BaseBox } from "../shared";

//styled()를 통해 해당 Component를 상속할 수 있다.
const Container = styled(BaseBox)`
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  padding: 35px 40px 25px 40px;
  margin-bottom: 10px;
  form { //하위 요소를 component를 만들지 않고 바로 style할 수 있다.
    margin-top: 35px;
    width: 100%;
    display: flex;
    justify-items: center;
    flex-direction: column;
    align-items: center;
  }
`;
//childrern은 하위 요소들을 다 포함 한다.
function FormBox({ children }) {
  return <Container>{children}</Container>;
}
export default FormBox;

#6.1 Helmet Component

App.js에서 최상위에 HelmetProvider라는 componenet를 생성한다.

//App.js
import { HelmetProvider } from "react-helmet-async";

function App() {
  const isLoggedIn = useReactiveVar(isLoggedInVar);
  const darkMode = useReactiveVar(isDarkModeVar);
  return (
    <HelmetProvider>
      <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
        <GlobalStyles />
        <Router>
          <Switch>
.
.
.

Helmet component를 이용한 title component를 만들어 다른 components에서 사용한다.

//PageTittle.js
import { PropTypes } from "prop-types";
import { Helmet } from "react-helmet-async";

function PageTitle({ title }) {
    return (
        <Helmet>
            <title>{title} | Instaclone</title>
        </Helmet>
    );
}

PageTitle.propTypes = {
    title: PropTypes.string.isRequired,
};

export default PageTitle;

#6.2 React Hook Form

useForm을 이용하면 Form에 관련된 유효성검사나 error처리 같은 여러 기능들을 사용할 수 있다.

useForm에서 mode를 설정할 수 있는데 크게 3가지가 있다.

  • onChange: 설정한 state의 변화가 있을 때마다 검사한다.
  • onBlur: focus가 해당 form으로부터 unfocus되었을 때 검사한다.
  • onTouched: 처음에는 blur로 event처리를 하다가 다음부터는 change로 event처리를 한다.
// Login.js
function Login() {
  const { register, handleSubmit, formState } = useForm({
    mode: "onChange",
  });
  const onSubmitValid = (data) => {
    console.log(data);
  };
  const onSubmitInvalid = (data) => {
    console.log(data, "invalid");
  };
  return (
    <AuthLayout>
      <PageTitle title="Login" />
      <FormBox>
        <div>
          <FontAwesomeIcon icon={faInstagram} size="3x" />
        </div>
        <form onSubmit={handleSubmit(onSubmitValid, onSubmitInvalid)}>
          <Input {...register("username", {
            required: "Username is required",
            minLength: {
              value: 5,
              message: "Username should be longer than 5 chars.",
            },
          })} type="text" placeholder="Username" 
	  hasError={Boolean(formState.errors?.username?.message)} />
          <FormError message={formState.errors?.username?.message} />
          <Input {...register("password", {
            required: "Password is required",
          })} type="password" placeholder="Password" 
	  hasError={Boolean(formState.errors?.password?.message)} />
          <FormError message={formState.errors?.password?.message} />
          <Button type="submit" value="Log in" disabled={!formState.isValid} />
        </form>

#6.3 Apollo Client

BE와 연결하기 위해 apollo client를 사용한다.
client를 생성하고 app.js에서 최상위에 ApolloProvider 태그를 만들어준다.

//apollo.js
export const client = new ApolloClient({
    uri: "http://localhost:4000/graphql",
    cache: new InMemoryCache(),
})
//App.js
function App() {
  const isLoggedIn = useReactiveVar(isLoggedInVar);
  const darkMode = useReactiveVar(isDarkModeVar);
  return (
    <ApolloProvider client={client}>
      <HelmetProvider>
        <ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
          <GlobalStyles />
          <Router>
.
.
.

#6.4 Login

graphql로 BE와 대화하기 위해 mutation을 만들고 useMutation을 통해 login함수와 loading변수를 가져온다.
useFrom의 getValues를 통해 form의 value들을 가져오고 setError를 통해 예외처리를 한다. 그리고 다시 clearErrors를 통해 에러를 없애준다.

//Login.js
const LOGIN_MUTATION = gql`
  mutation login($username: String!, $password: String!){
    login(username: $username, password: $password){
      ok,
      token,
      error,
    }
  }
`;

function Login() {
  const { register, handleSubmit, formState, setError, clearErrors, getValues } = useForm({
    mode: "onChange",
  });

  const onCompleted = (data) => {
    const { login: {
      ok, token, error
    } } = data;
    if (!ok) {
      return setError("result", {
        message: error
      });
    }
    if (token) {
      logUserIn(token);
    }
  };

  const [login, { loading }] = useMutation(LOGIN_MUTATION, {
    onCompleted,
  });

  const onSubmitValid = (data) => {
    if (loading) {
      return;
    }
    const { username, password } = getValues();

    login({
      variables: { username, password }
    });
  };
.
.
.

token을 localStorage에 저장하고 확인할 수 있도록 한다. 새로고침을 해도 로그인이 유지되도록 한다.

//apollo.js
const TOKEN = "token";

export const isLoggedInVar = makeVar(Boolean(localStorage.getItem(TOKEN)));
export const logUserIn = (token) => {
    localStorage.setItem(TOKEN, token);
    isLoggedInVar(true);
};
export const logUserOut = () => {
    localStorage.removeItem(TOKEN);
    isLoggedInVar(false);
};

#6.5 Sign Up

회원갑입도 위와 동일하게 만들어준다. 그리고 회원가입 성공시 Login창으로 message, username, password를 가지고 넘어간다.

history를 통해 보내준다.

//Signup.js
const history = useHistory();
    const onCompleted = (data) => {
        const { username, password } = getValues();
        const {
            createAccount: { ok, error },
        } = data;
        if (!ok) {
            return setError("result", {
                message: error
            });
        } // (path, 넘길 인자들)
        history.push(routes.home, {
            message: "Account created. Please log in.",
            username,
            password,
        });
    };

location을 통해 받는다.

//Login.js
const location = useLocation();

const { register,
       handleSubmit, 
       formState, 
       setError, 
       clearErrors, 
       getValues 
      } = useForm({
  mode: "onChange",
  defaultValues: {
    username: location?.state?.username || "",
    password: location?.state?.password || "",
  },
});
profile
뿌셔뿌셔

0개의 댓글