Blogrow Project Day 01 (2)

thisisyjin·2022년 5월 19일
0

Dev Log 🐥

목록 보기
10/23

Blogrow Project

📝 DAY 01 - 220518 (2)

  • 리덕스 상태관리

리덕스 상태관리

  • 회원가입, 로그인 폼(form)의 상태 관리.

modules/auth.js 수정

import { createAction, handleActions } from 'redux-actions';
import produce from 'immer';

const CHANGE_FIELD = 'auth/CHANGE_FIELD';
const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';

export const changeField = createAction(
  CHANGE_FIELD,
  ({ form, key, value }) => ({ form, key, value }), // form: register, login / key: username, password, passwordConfirm
);
export const initializeForm = createAction(INITIALIZE_FORM, (form) => form);
// register, login

const initialState = {
  register: {
    username: '',
    password: '',
    passwordConfirm: '',
  },
  login: {
    username: '',
    password: '',
  },
};

const auth = handleActions(
  {
    [CHANGE_FIELD]: (state, { payload: { form, key, value } }) =>
      produce(state, (draft) => {
        draft[form][key] = value; // ex> state.register.username
      }),
    [INITIALIZE_FORM]: (state, { payload: form }) => ({
      ...state,
      [form]: initialState[form],
    }),
  },

  initialState,
);

export default auth;

1. createAction()

  • redux-actions의 createActions 함수.
    -> 첫번째 인자로는 type이 들어가고 / 두번째 인자로는 payload로 들어갈 것들의 이름을 넣어줌.
  • 원래는 payload: { form: 123, key: 456, value: 789 } 와 같이 객체로 저장되지만,
  • 구조분해 할당으로 ({ form, key, value }) => ({ form, key, value }) 와 같이 해줌.

2. handleActions()

  • 첫번째 인자로 들어가는 객체의 각 필드에 액션타입을 적어주고, 값으로는 state 업데이트함수를 넣어줌.
    -> 여기서는 immer의 produce() 함수로 쉽게 (spread ... 없이) 불변성을 유지함

  • 두번째 인자로는 초기 값인 initialState가 들어감.

3. immer 라이브러리의 produce 함수

  • 참고 링크
  • 첫번째 인자는 state(리듀서 함수의 인자로 들어오는 state)
  • 두번째 인자는 recipe. 첫번째 인자(=state)가 어떻게 바뀔지 정의하는 함수임.
produce(state, (draft) => {
        draft[form][key] = value; // ex> state.register.username
   })

컨테이너 컴포넌트 생성

  • 컴포넌트를 리덕스와 연동시킴.
    -> connect() 함수 대신에
    react-redux의 Hook인 useDispatch, useSelector 이용.

지난 포스팅 참조 - useDispatch, useSelector(state 조회)

src/containers 폴더 생성 후, auth/LoginForm 파일 생성.

LoginForm (컨테이너) 생성

containers/auth/LoginForm

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { changeField, initializeForm } from '../../modules/auth';
import AuthForm from '../../components/auth/AuthForm';

const LoginForm = () => {
  
  // store.dispatch 사용하기 위해 가져옴
  const dispatch = useDispatch();
  
  // useSelector로 state.auth.login 을 가져옴. (state는 register, login 필드 존재)
  const { form } = useSelector(({ auth }) => ({
    form: auth.login,
  }));

  // input 변경시 이벤트
  const onChange = (e) => {
    const { value, name } = e.target;
    // changeField 액션을 발생시킴. -> 인자로는 payload가 들어감.
    dispatch(
      changeField({ 
        form: 'login',  
        key: name,	// e.target.name 으로, username 또는 password
        value, 
      }),
    );
  };

  const onSubmit = (e) => {
    e.preventDefault(); 
    // 폼 제출시 👉 추후 작성예정.
  };

  useEffect(() => {
    // initializeForm 액션 생성함수를 dispatch함. - payload는 login (form: login)
    dispatch(initializeForm('login'));
  }, [dispatch]);

  return (
    // props로 넘긴 login값에 따라 UI 변경됨.
    <AuthForm
      type="login"
    // 🔻 새로운 props들 넘김
      form={form}
      onChange={onChange}
      onSubmit={onSubmit}
    />
  );
};

✅ 액션 발생(dispatch)시
dispatch(액션생성함수(payload))를 해주면 됨

LoginPage 렌더링

  • AuthForm 대신에 LoginForm(=컨테이너)를 넣어줌.

pages/LoginPage.js 수정

import AuthTemplate from '../components/auth/AuthTemplate';
import LoginForm from '../containers/auth/LoginForm';

const LoginPage = () => {
  return (
    <AuthTemplate>
      <LoginForm />
    </AuthTemplate>
  );
};

export default LoginPage;

AuthForm 수정

components/auth/AuthForm.js 수정

import styled from 'styled-components';
import palette from '../../lib/styles/palette';
import { Link } from 'react-router-dom';
import Button from '../common/Button';

	... 

const AuthForm = ({ type, form, onChange, onSubmit }) => {
  const text = textMap[type];
  return (
    <AuthFormBlock>
      <h3>{text}</h3>
      <form onSubmit={onSubmit}>
        <StyledInput
          autoComplete="username"
          name="username"
          placeholder="아이디"
          onChange={onChange}
          value={form.username}
        />
        <StyledInput
          type="password"
          autoComplete="new-password"
          name="password"
          placeholder="비밀번호"
          onChange={onChange}
          value={form.password}
        />
        {type === 'register' && (
          <StyledInput
            autoComplete="new-password"
            name="passwordConfirm"
            placeholder="비밀번호 확인"
            type="password"
            onChange={onChange}
            value={form.passwordConfirm}
          />
        )}
        <ButtonWithMarginTop teal fullWidth>
          {text}
        </ButtonWithMarginTop>
      </form>
      <Footer>
        {type === 'login' ? (
          <Link to="/register">회원가입</Link>
        ) : (
          <Link to="/login">로그인</Link>
        )}
      </Footer>
    </AuthFormBlock>
  );
};

export default AuthForm;
  • LoginForm(컨테이너)에서 넘겨준 props를 받아오고 값을 사용함.
const { form } = useSelector(({ auth }) => ({
    form: auth.login,
  }));
  • props로 전달된 form은 state, 즉 auth.login 이므로 username, password 필드를 가진 객체이다.
login: {
    username: '',
    password: '',
  },

✅ TEST

  • localhost:3000/login 에서 인풋에 텍스트 입력 후,
    리덕스 스토어에 값이 잘 들어가는 것을 확인.


RegisterForm (컨테이너) 생성

  • LoginForm 을 그대로 복제해서
    Login -> Register,
    login -> register 로 바꿔주면 됨.

containers/RegisterForm.js

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { changeField, initializeForm } from '../../modules/auth';
import AuthForm from '../../components/auth/AuthForm';

const RegisterForm = () => {
  const dispatch = useDispatch();
  const { form } = useSelector(({ auth }) => ({
    form: auth.register,
  }));

  const onChange = (e) => {
    const { value, name } = e.target;
    dispatch(
      changeField({
        form: 'register',
        key: name,
        value, // value: e.target.value
      }),
    );
  };

  const onSubmit = (e) => {
    e.preventDefault();
    // 폼 제출시
  };

  useEffect(() => {
    dispatch(initializeForm('register'));
  }, [dispatch]);

  return (
    <AuthForm
      type="register"
      form={form}
      onChange={onChange}
      onSubmit={onSubmit}
    />
  );
};

export default RegisterForm;

마찬가지로 RegisterPage 렌더링을 바꿔줌.

pages/RegisterPage.js

import AuthTemplate from '../components/auth/AuthTemplate';
import RegisterForm from '../containers/auth/RegisterForm';

const RegisterPage = () => {
  return (
    <AuthTemplate>
      <RegisterForm />
    </AuthTemplate>
  );
};

export default RegisterPage;

RESULT

스토어에 잘 저장됨!

우선 여기까지 진행 과정 복습하기.
내일은 redux 미들웨어인 redux-saga 로 API 연동 + 회원가입 / 로그인 구현

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글