티스토리 뷰

Node.js/react

Redux - Reducer, Action

kkoon9 2020. 9. 27. 17:42

1️⃣ Reduce ❗

reducer는 Store의 문지기’라고 생각하면 된다.

reducer는 이전 상태와 Action을 합쳐, 새로운 state를 만드는 조작을 말한다.

2️⃣ Action ❗

Store 및 Store에 존재하는 State에 직접 접근하기 위해 Action을 꼭 거쳐야 한다.

  1. Store에 대해 뭔가 하고 싶은 경우엔 Action을 발행한다.
  2. reducer가 Action의 발생을 감지하면, State가 경신된다.

Action은 기본적으로 {type, payload} 포맷을 갖고 있는 오브젝트가 된다.

 

다음은 예시 코드이다.

reducers/index.js

import { combineReducers } from 'redux';
import alert from './alert'; // [1]
import auth from './auth'; // [2]
import profile from './profile'; // [3]
import post from './post'; // [4]

export default combineReducers({
  alert,
  auth,
  profile,
  post
});

[1]. alert

// reducers/alert.js
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';

const initalState = [];

export default function (state = initalState, action) {
  const { type, payload } = action;

  switch (type) {
    case SET_ALERT:
      return [...state, action.payload];
    case REMOVE_ALERT:
      return state.filter(alert => alert.id !== payload);
    default:
      return state;
  }
}

initailState와 action을 매개변수로 받는다.

action는 type, payload로 구성된다.

// actions/alert.js
import uuid from 'uuid';
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';

export const setAlert = (msg, alertType, timeout = 5000) => dispatch => {
  const id = uuid.v4();
  dispatch({
    type: SET_ALERT,
    payload: { msg, alertType, id }
  });

  setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
}

Action에서 type에 따라 dispatch를 통해 나누어 보내준다.

Alert은 다른 Action(ex. post, auth etc)에 알림을 줄 때 활용된다.

[2]. auth

// reducers/auth.js
import {
  REGISTER_SUCCESS,
  REGISTER_FAIL,
  AUTH_ERROR,
  USER_LOADED,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  ACCOUNT_DELETED
} from "../actions/types";

const initialState = {
  token: localStorage.getItem('token'),
  isAuthenticated: null,
  loading: true,
  user: null
}

export default function (state = initialState, action) {
  const { type, payload } = action;

	// Switch 부분 [2-n]
}

initialState에 token, isAuthenticated, loading, user를 넣어준다.

initialState는 각 type에 해당할 때 사용된다.

// actions/auth.js
import axios from 'axios';
import { setAlert } from './alert';
import setAuthToken from '../utils/setAuthToken';
import {
  REGISTER_SUCCESS,
  REGISTER_FAIL,
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  CLEAR_PROFILE,
} from "./types";

// reducers에 해당하는 [2-n]

[2-1]. USER_LOADED

// reducers/auth.js
switch (type) {
    case USER_LOADED:
      return {
        ...state,
        isAuthenticated: true,
        loading: false,
        user: payload
      }
  }

// actions/auth.js
export const loadUser = () => async dispatch => {
  if (localStorage.token) { // [2-1-1]
    setAuthToken(localStorage.token); // header에 x-auth-token : token
  }

  try {
    const res = await axios.get('/api/auth');

    dispatch({
      type: USER_LOADED,
      payload: res.data
    });
  } catch (error) {
    dispatch({
      type: AUTH_ERROR
    });
  }
}

axios는 유저 정보를 가져온다.

[2-1-1]. token을 setAuthToken으로 header에 넣어줄 수 있다.

setAuthToken

import axios from 'axios';

const setAuthToken = (token) => {
  if (token) {
    axios.defaults.headers.common['x-auth-token'] = token;
  } else {
    delete axios.defaults.headers.common['x-auth-token'];
  }
}

export default setAuthToken;

[2-2]. Success

// reducers/auth.js
switch (type) {
    case REGISTER_SUCCESS:
    case LOGIN_SUCCESS:
      localStorage.setItem('token', payload.token); // [2-2-1]
      return {
        ...state,
        ...payload,
        isAuthenticated: true,
        loading: false,
      }
  }

// actions/auth.js

// Register User
export const register = ({ name, email, password }) => async dispatch => {
  const config = {
    headers: {
      'Content-Type': 'application/json'
    }
  }

  const body = JSON.stringify({ name, email, password });

  try {
    const res = await axios.post('/api/users', body, config);

    dispatch({
      type: REGISTER_SUCCESS,
      payload: res.data
    });
    dispatch(loadUser());

  } catch (error) {
    const errors = error.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
    }
    dispatch({
      type: REGISTER_FAIL
    })
  }
}

// Login User
export const login = ({ email, password }) => async dispatch => {
  const config = {
    headers: {
      'Content-Type': 'application/json'
    }
  }

  const body = JSON.stringify({ email, password });

  try {
    const res = await axios.post('/api/auth', body, config);

    dispatch({
      type: LOGIN_SUCCESS,
      payload: res.data
    });
    dispatch(loadUser());

  } catch (error) {
    const errors = error.response.data.errors;

    if (errors) {
      errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
    }
    dispatch({
      type: LOGIN_FAIL
    })
  }
};

[2-2-1]. localStorage.setItem('token', payload.token);

회원가입, 로그인이 완료하면 token을 initialState에 저장해준다.

[2-3]. Fail

// reducers/auth.js
switch (type) {
    case REGISTER_FAIL:
    case AUTH_ERROR:
    case LOGIN_FAIL:
    case LOGOUT:
    case ACCOUNT_DELETED:
      localStorage.removeItem('token');
      return {
        ...state,
        toekn: null,
        isAuthenticated: false,
        loading: false,
      }
    default:
      return state;
  }

// actions/auth.js

// Logout / Clear Profile
export const logout = () => dispatch => {
  dispatch({
    type: LOGOUT
  });
  dispatch({
    type: CLEAR_PROFILE
  });
}


references

kkoon9/Full-Stack-With-javascript

아마 이게 제일 이해하기 쉬울걸요? React + Redux 플로우의 이해

'Node.js > react' 카테고리의 다른 글

react-redux Flow  (0) 2020.09.28
Redux - connect  (0) 2020.09.28
Redux - Store  (0) 2020.09.27
Middleware와 thunk  (0) 2020.09.27
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함