Skip to content

Instantly share code, notes, and snippets.

@qkreltms
Last active March 22, 2019 12:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qkreltms/731f00616c33abd30e57da8eafdff212 to your computer and use it in GitHub Desktop.
Save qkreltms/731f00616c33abd30e57da8eafdff212 to your computer and use it in GitHub Desktop.
React-Redux 정리

1. 개요

Redux란 앱의 데이터가 변경되는 모든 상태관리를 store안의 object tree 한 곳에서 처리할 수 있게 한다. state를 바꾸는 방법은 action으로 값을 보내고 처리하는 방법을 reducer에 작성한다.

The whole state of your app is stored in an object tree inside a single store. The only way to change the state tree is to emit an action, an object describing what happened. To specify how the actions transform the state tree, you write pure reducers.

참고: redux 공식 깃허브

필자가 Vue로 앱을 만들 때 규모가 점점 커지면서 상태관리가 힘들었다. 컴포넌트 여러개로 쪼개다보면, 다리 2개를 건너서 상태를 주고 받기도 했고, 드물지만 먼 곳에서 상태를 가져올 때는 구현하기가 까다로웠다. Vue판 Redux인 Vuex를 사용하면 이러한 문제가 해결이 되지만, 러닝커브와 프로젝트 기한이 다가와 적용하지 못해서 아쉬웠다. 이번 기회에 React를 통해 Redux를 배워보기로 했다.

2. 설명

들어가기에 앞서, 간단히 설명한다.

todos 라는 값이 있다고 가정하자.

    todos: [
            {id: 1, msg: "todo1"},
            {id: 2, msg: "todo2"},
          ],

이 값에 {id:3, msg: "todo3"} 데이터를 추가하고 싶다면 먼저 아래의 Action을 sotre안의 object tree로 발송(dispatch)해야 한다.

{ type: 'ADD_TODO', todo: {id:3, msg: "todo3"} }

*Real-World에서는 Action을 위의 식으로 하기보다 함수(ActionCreator)에서 반환받는 식으로 사용한다.

    // ex) todo = {id:3, msg: "todo3"}
    
    export const setTodo = (todo) => {
        return {
            todo,
            type: 'ADD_TODO',
        };
    };

참고로 Action은 순수한 함수여야 된다. 즉, Ajax call을 하면 안된다.(미리설명하자면 Ajax call은 Thunk라는 라이브러리를 통해 Action에서 구현)

이제 이 Action을 object tree에서 받아서 todos에 3번째 todo를 포함시키도록 잘 처리해줘야 하는데 이 역할을 Reducer가 한다.

    // 여기의 todos는 state로 생각하면 된다.  
    todos: [
            {id: 1, msg: "todo1"},
            {id: 2, msg: "todo2"},
          ],
          
    function todosReducer(state = todos, action) {
      switch (action.type) { //전달받은 Action의 type과 값(todo)으로 적절한 작업 실행.
        case 'ADD_TODO':
          const newTodo = [...state, action.todo]
          return newTodo
        default:
          return state
      }
    }
  1. state = todos 가 가능한 이유는 state의 처음값이 undefined가 나오기로 설계되었기 때문이다.

  2. 기존의 state에 데이터를 추가하는 것 보다, 새로운 state를 만들어서 반환하는 것을 "권장"한다. (위에서는 새로운 배열로 만들어 반환함.)

state 변수가 2개 이상일 경우

*만약 state 변수가 2개 이상일 경우 passwordVisibility: state.passwordVisibility 처럼 state값을 반환해야 undefined가 나오지 않게 할 수 있다.(또는 ...state)

import { IPasswordAction, SET_PASSWORD, SHOW_PASSWORD } from "../actions";

export interface IPasswordState {
    password: string;
    passwordVisibility: boolean;
}

const createEmpty = () => ({
    password: "",
    passwordVisibility: false,
});

export const passwordReducer = (state = createEmpty(), action: IPasswordAction) => {
    switch (action.type) {
        case SET_PASSWORD: {
            return {
                password: action.password,
                passwordVisibility: state.passwordVisibility,
            } as IPasswordState;
        }

        case SHOW_PASSWORD: {
            return {
                password: state.password,
                passwordVisibility: action.passwordVisibility,
            } as IPasswordState;
        }

        default:
            return state;
    }
};

Reducer가 todo에 관련된 것 뿐만아니라 다른 것도 있을 수 있는데 이럴 경우 Reducer를 합쳐주는 cobineReducers를 사용한다.

    export const rootReducer = combineReducers({
        todo: todoReducer,
        locale: localeReducer,
    }

이제 이 Reducer와 Action을 사용하기 connect함수를 활용한다.

    // 코드 생략...
    const TodoList = (props) => {
    // props로 setTodo 함수와 todos가 전달된다. todos는 Read-Only 이므로 값을 바꾸려면 setTodo 함수를 이용한다.
    	props.setTodo({id:3, msg: "todo3"});
    	console.log(props.todos);
    // 코드 생략...
    }
    
    const mapStateToProps = (state) => ({
        todos: state.todo.todos, //state로 합쳐진 Reducer가 제공되고, 그 안의 state값 불러옴
    });
    
    const mapDispatchToProps = (dispatch) => ({
      setTodo: (todo) => dispatch(setTodo(todo)), //object tree로 dispatch 하고싶은 Action 호출
    });
    
    export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

만약 mapStateToProps, mapDispatchToProps, connect 같은 redux에 관련된 코드를 분리 하고 싶다면 TodoList 컴포넌트를 함수형 컴포넌트로 만들어 프레젠테이셔널 컴포넌트로 사용하고

앞에서 제시한 redux 관련 코드를 따로 TodoContainer.js 파일을 만들어 컨테이너컴포넌트로 분리한다.

참고: 블로그

마지막으로 Provider를 이용해 store을 App에 넣어준다.

    // 코드 생략...
      <Provider store={store}>
        <App />
      </Provider>

3. 정리

  1. connect 함수로 state(setTodo(), todos, 등...) 값을 Reducer으로 부터 전달 받고,
  2. Action으로 todo 값을 보낸다(dispatch).
  3. Reducer로 type을 구분 후 todo 값을 바꾸는 것과 같은 적절한 처리를 한다.
@sspcoolguy
Copy link

good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment