[TIL] Redux

Ha Young Do·2021년 8월 4일
0

Redux

Redux는 상태를 관리해 주는 라이브러리이다. React와 꼭 같이 쓸 필요는 없지만 같이 쓸 때 더욱 시너지가 난다.

Redux는 단방향 데이터 흐름을 따른다. State는 현재의 어플리케이션의 상태를 말하고, 그것을 토대로 View가 render된다. 만약 View에서 어떤 이벤트가 발생한다면 Action을 통해 새로운 State가 업데이트 된다. 그 새로운 State를 토대로 View가 다시 render된다.

Three Principles of Redux

  1. single source of truth
    데이터 혹은 상태를 관리할 때 각각의 component가 아닌 단일 공간에 모아서 관리한다. 여러 component에서 하나의 상태를 사용할 때 상태를 올려 주고 내려 주는 과정 없이 store에 바로 접근하여 사용 가능하다.

  2. state is read-only
    상태는 직접적으로 변경이 불가능하고 action을 통해 새로운 상태 객체를 받아 store에 전달해 주어야 한다.

  3. changes are made with pure functions
    상태를 변화시키는 reducer는 순수 함수로만 작성되어야 한다.

Store

상태가 관리되는 오직 하나의 공간, component와 별개로 운영되는 공간으로 각각의 component에서 state가 필요할 때 store에 접근해서 가져올 수 있다.

const store = createStore(reducer, [preloadedState], [enhancer])

createStore() method를 이용하여 store를 생성할 수 있다. createStore()의 첫번째 인자로는 reducer들을, 두번째 인자로는 기본 state 값, 세번째 인자로는 middleware를 사용하게 해 주는 enhancer를 넣는다.

Action

Action은 store에 새로운 상태를 만들 데이터를 운반해 주는 역할을 한다. Type을 비롯한 상태 관련 데이터가 담기는 JavaScript 객체로 작성된다. (Type은 필수 작성)

const newTodoItem = 'Learn React';
  
const addTodo = (todoItem) => {
  return {
    type: 'ADD_TODO',
    payload: newTodoItem
  };
}

const newTodoAction = addTodo(newTodoItem);

위 코드는 to-do list에 새로운 할 일을 추가할 때 사용되는 action이다. Type을 ADD_TODO로 지정해 주어 해당 action이 생성되면 새로운 할 일을 추가해야 한다는 것을 알린다. Payload에는 필요한 추가적인 데이터를 저장해 전달하는데, 이 경우 새로운 할 일의 내용을 저장해 주었다.

Reducer

Reducer는 action에 담긴 데이터를 store에 전달해 줄 때 거치는 순수 함수이다. Reducer는 action 객체를 읽어 새로운 상태를 반환해 store에 저장된 현 상태를 대체한다.
Redux로 쓰여진 앱은 실제로 단 하나의 root reducer를 가진다. 하나의 reducer method에서 action의 type에 따라 처리 방법을 달리하는 식으로 만든다.

export default function appReducer(state = initialState, action) {
  switch (action.type) {
    case 'ADD_TODO': {
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            content: action.payload,
            completed: false
          }
        ]
      }
    }
    case 'SOME_OTHER_ACTION_TYPE': {
      // handle some other action type
    }
    default:
      return state
  }
}

위의 reducer function은 action과 현재 state를 받아, action에 담긴 데이터를 읽어 새로운 state를 생성한다. 이 때 action.type에 담긴 type에 따라 다르게 처리하도록 설계한다.

Dispatch

Action 객체가 dispatch method에 전달되고 dispatch가 reducer를 호출해서 새로운 state를 생성한다.

// current state is as follows:
// { todos: [ (list of to-dos in object form) ] }

function todoList() {
  const todos = useSelector(state => state.todos);
  const dispatch = useDispatch();
  
  const handleSubmit = (e) => {
    e.preventDefault();
    const todoItem = e.target.elements.todo.value
    dispatch(addTodo(todoItem));
  }

  return (
    <div>
      <ul id="todoList">
        {todos.map((todo, index) => (
          <li key={index}>{todo.content}</li>
        ))}
      </ul>
      <form onSubmit={handleSubmit}>
        <input type="text" id="todo" />
        <button type="submit">Add To-do</button>
      </form>
    </div>
  )
}

위의 to-do list를 표현한 component에서, 새로운 할 일을 입력하고 submit 버튼을 누르면 handleSubmit() event handler가 호출된다. 이 method는 dispatch method를 호출하여 ADD_TODO type을 가진 action을 생성해 인자로 넣어 준다. 이 때 dispatch method 내부에서는 reducer가 호출되어 새로운 state가 생성된다.

react-redux

위의 코드에서는 Redux와 함께 쓰이는 React Hooks method들이 쓰였다. react-redux library를 import 해 와 쓸 수 있다.

  • useSelector()
    Redux store에 저장된 state로부터 데이터를 불러오는 method이다.
    첫번째 인자로는 selector function, 즉 state에서 필요한 데이터를 선택하는 함수를 넣는다.
    두번째 인자는 optional로, equality function, 즉 현재 state와 새로운 state를 비교하는 함수를 넣는다. 만약 false를 return하면, component가 강제 re-render된다.

  • useDispatch()
    Redux dispatch method로, 인자로 action을 넣는다.

Redux Data Flow


1. UI에서 클릭 이벤트가 발생한다.
2. event handler에 담긴 dispatch method가 실행되어 action 객체가 생성된다.
3. action 객체가 reducer method를 통해 new state를 생성하여 store에 새로 저장한다.

Async in Redux (redux-thunk)

미들웨어란 리듀서를 실행하기 전후에 특정 액션에 특화된 작업을 처리해 줄 수 있는 역할을 한다. 여러 가지 감시 기능을 다중적으로 처리할 수 있고, 연쇄 효과를 효울적으로 관리할 수 있다.
redux-thunk 미들웨어를 사용하여, action을 function 형태로 작성한다면, reducer에 전달하지 않고 dispatch와 getState()를 action 함수 안에 패싱하여 함수 안에서 그들을 실행시키는 구조로 비동기 처리가 가능하다. 이 경우 호출된 순서대로 차곡차곡 dispatch하고 reducer로 흘러서 스토어에 영향을 주게 된다.

Why Redux?

  1. 상태를 예측 가능하게: Reducer가 순수 함수이기 때문에 새로운 state를 예측하기가 쉽다.
  2. 유지보수가 쉽다: State를 component에 저장해 주는 것이 아니라 한 곳의 store에 저장하면 props drilling이 필요 없기 때문이다.
  3. Debugging 유리: Redux Devtools와 같은 도구를 이용하여 action, state를 logging하여 추적 가능하다.

https://blog.codecentric.de/en/2017/12/developing-modern-offline-apps-reactjs-redux-electron-part-3-reactjs-redux-basics/
https://redux.js.org/tutorials/
https://blog.logrocket.com/how-to-convert-your-existing-redux-containers-to-hooks/

profile
Codestates Software Engineering Full IM 28th

0개의 댓글