웹찢남

REACT 12일차 (리덕스) 본문

FRONT_END/REACT 공부

REACT 12일차 (리덕스)

harry595 2020. 7. 14. 19:36

리덕스란 컨텍스트보다 조금 더 체계적으로 데이터를 관리할 수 있도록 한다.

리덕스는 데이터를 스토어라는 곳에서 관리한다.

 

프로퍼티의 경우 상위 컴포넌트에서 하위 컴포넌트로 전달되는 읽기 전용 데이터

state는 컴포넌트의 state를 저장하고 변경할 수 있는 데이터다.

컨텍스트는 부모 컴포넌트에서 생성하여 모든 자식 컴포넌트에 전달하는 데이터

리덕스는 서버에서 받은 데이터를 앱 전체에 전달하거나 관리한다.

 

컴포넌트는 dispatch 함수를 통해 리듀서에 액션을 전달한다. 액션에는 리듀서에서

처리해야 할 작업의 이름과 데이터가 객체 형태로 들어있다.

리듀서는 액션을 받아 스토어 변경 작업을 진행한다. 스토어 변경 작업이 완료되면

connect 함수로 연결된 컴포넌트에 변경된 스토어의 데이터를 전파하여 동기화한다.

 

1. 리덕스 설치하기

yarn add redux react-dedux

 

2. 리덕스 크롬 확장 도구 설치하기

Redux DevTools -> Add to chrome

 

3. 리덕스 개발자 도구 설치

yarn add redux-devtools-extension --dev

 

1. 스토어 생성하기

스토어를 생성할떄 createStore()을 이용하여 생성할 수 있다.

지금은 작업을 수행하지 않을 예정이므로 createStore(state=>state)를 사용한다.

 

import React, { PureComponent } from "react";
import { createStore } from "redux";
import { Provider } from "react-redux";

class ReduxApp extends PureComponent {
  store = createStore(
    (state) => state,
    { loading: false, name: "test" },
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
  );

  render() {
    return <Provider store={this.store}>리덕스 예제</Provider>;
  }
}

export default ReduxApp;

리덕스의 createStore를 import한 후 Provider 컴포넌트를 사용하여 스토어 데이터를 하위 컴포넌트에 전달한다.

또한 리듀서를 createStore() 인자로 전달한다.

 

 

위처럼 리덕스 크롬 확장 도구를 통해 스토어 데이터를 볼 수 있다.

 

액션과 리듀서의 관계

 

액션은 {type: ~,oayload: ~} 구조로 된 객체이다. type은 액션이 어떤 작업인지

쉽게 이해할 수 있는 고유할 값을 구분한 문자열로 넣어준다.

 

리듀서는 function reducer(state, action) {return state;}의 구조를 가지며

스토어의 state, 액션을 받아 새로운 스토어의 데이터를 반환한다.

const reducer = (state,action) => state +action.payload;

이때 리듀서가 반환하는 값의 자료형은 스토어의 이전 데이터와 동일해야한다.

 

액션은 dispatch 함수를 통해 리듀서로 전달된다. 

 

import React, { PureComponent } from "react";
import { Provider } from "react-redux";
import { createStore } from "redux";

const reducer = (state, action) => {
  const { type, payload } = action;
  switch (type) {
    case "SET_LOADING": {
      return {
        ...state,
        loading: payload,
      };
    }
    case "RESET_LOADING": {
      return { ...state, loading: false };
    }
    case "SET_USER": {
      return {
        ...state,
        user: payload,
      };
    }
    default:
      return state;
  }
};

class ReduxApp extends PureComponent {
  store = createStore(
    reducer,
    { loading: false, name: "test" },
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
  );

  componentDidMount() {
    this.store.dispatch({
      type: "SET_LOADING",
      payload: true,
    });
    this.store.dispatch({
      type: "RESET_LOADING",
    });
    this.store.dispatch({
      type: "SET_USER",
      payload: { name: "Park", age: 20 },
    });
  }

  render() {
    return <Provider store={this.store}>리덕스 예제</Provider>;
  }
}
export default ReduxApp;

type이 reset_loading인 경우 스토어 데이터의 loading값을 false로

set_user인 경우 payload값으로 user의 값을 변경하고

Reset_loading에 해당하는 액션을 호출한다.

SET_USER에 해당하는 액션을 payload와 함께 호출한다.

또한 componentDidBount 함수 내에서 액션이 dispatch함수를 통해 리듀서에 전달되는 것을 볼 수 있다.

 

리듀서 분리하기

지금까지의 리듀서는 하나의 리듀서가 스토어 전체를 관리한다.

앞에서는 loading,user의 데이터를 관리하므로 액션과 리듀서를 분리하여 만들어 보자

 

import { SET_LOADING, RESET_LOADING } from '../actions/loadingActions';

const initState = false;

export default function reducer (state = initState, action) {
  const { type, payload } = action;
  switch(type) {
    case SET_LOADING: {
      return payload;
    }
    case RESET_LOADING: {
      return initState;
    }
    default:
      return state;
  }
};
import { SET_USER } from '../actions/userActions';

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

  switch(type) {
    case SET_USER: {
      return {
        ...state,
        ...payload,
      };
    }
    default:
      return state;
  }
}

위처럼 loading의 reducer와 user reducer을 분리할 수 있다.

 

import loading from './loadingReducer';
import user from './userReducer';

export default {
  loading,
  user,
};

또한 두 리듀서가 하나처럼 동작하기 위해서는 위와 같은 index.js파일을 만들어 익스포트한다.

 

import { createStore, combineReducers } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducers from './reducers';

export default initStates => createStore(
  combineReducers(reducers),
  initStates,
  composeWithDevTools(),
);

마지막으로 스토어 설정파일을 만든다.

이때 여러개의 리듀서를 combinereducers 함수로 묶어 createStore의 인자로 전달한다.

웹팩은 폴더의 index파일을 자동으로 참조하므로 앞에서 작성한 index.js파일은

import reducers from './reducers'와 같이 작성해도 자동으로 참조된다.

 

리듀서를 나눴기때문에 본 코드도 수정을 해야한다. 코드는 다음과 같이 바꾸면 된다.

 

import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';
import configureStore from './configureStore';

class AdvReduxApp extends PureComponent {
  store = configureStore({ loading: false });

  componentDidMount() {
    this.store.dispatch({
      type: 'SET_LOADING',
      payload: true,
    });
    this.store.dispatch({
      type: 'RESET_LOADING',
    });
    this.store.dispatch({
      type: 'SET_USER',
      payload: { name: 'Park', age: 20 },
    });
  }

  render() {
    return <Provider store={this.store}>리덕스 예제</Provider>;
  }
}
export default AdvReduxApp;

 

reducer을 분리했듯이 action도 분리를 할 수 있다.

export const SET_LOADING = 'loading/SET_LOADING';
export const RESET_LOADING = 'loading/RESET_LOADING';

export const setLoading = loading => ({
  type: SET_LOADING,
  payload: loading,
});

export const resetLoading = () => ({
  type: RESET_LOADING
});

 

위처럼 action도 분리하면 reducer의 코드를 변경해야한다.

위에 이미 loadingReducer을 이에 맞게 올렸다..

이런식으로 user의 action도 분리하면된다.

 

마지막으로 리액트 앱도 이에 맞춰 수정을 하면 된다.

import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';
import configureStore from './configureStore';
import { setLoading, resetLoading } from './actions/loadingActions';
import { setUser } from './actions/userActions';

class AdvReduxApp extends PureComponent {
  store = configureStore({ loading: false });

  componentDidMount() {
    this.store.dispatch(setLoading(true));
    this.store.dispatch(resetLoading());
    this.store.dispatch(setUser({ name: 'Park', age: 20 }));
  }

  render() {
    return <Provider store={this.store}>리덕스 예제</Provider>;
  }
}
export default AdvReduxApp;

 

'FRONT_END > REACT 공부' 카테고리의 다른 글

REACT 11일차 (컨텍스트)  (0) 2020.07.10
REACT 10일차 (하이어오더 컴포넌트)  (0) 2020.07.09
REACT 9일차 (Style component)  (0) 2020.07.07
REACT 8일차 (material design)  (0) 2020.05.23
REACT 7일차 (스토리 북)  (0) 2020.05.22
Comments