ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Redux] 아주 아주 간단한 리덕스 기본 개념 3
    개발/React 2022. 7. 8. 19:39

    필수 사항은 아니지만 리덕스과 리액트를 같이 사용할 때

    Presentational component(이하 p.com)와 Container component(이하 c.com)를 분리하는 것이 정석에 가깝습니다.

    p.com은 props를 전달받아 UI에만 집중한 컴포넌트이며

    c.com은 리덕스에 있는 상태를 조회하거나 액션을 디스패치하는 컴포넌트입니다.

    즉, c.com에서 세팅을 한 후 p.com을 호출하면서 필요한 값을 props로 내려줍니다.

     

    Ducks 패턴을 사용해 하나의 파일(모듈이라 부름)에

    액션 함수, 액션 타입, 리듀서, 상태를 모아 놓았습니다(todos.js)

    이 모듈을 combineReducers 함수를 이용해서 rootReducer을 만든다고 해봅시다.

     

    todos.js(모듈)

    const ADD_TODO = 'todos/ADD_TODOS';
    const TOGGLE_TODO = 'todos/TOGGLE_TODO';
    
    let nextId = 1;
    export const addTodo = (text) => ({
      type: ADD_TODO,
      todo: {
        id: nextId++,
        text,
      },
    });
    
    export const toggleTodo = (id) => ({
      type: TOGGLE_TODO,
      id,
    });
    
    const initialState = [
      /*
        {
            id: 1,
            text: 'text',
            done: false
            
        }
      */
    ];
    
    export default function todos(state = initialState, action) {
      switch (action.type) {
        case ADD_TODO:
          return state.concat(action.todo);
        case TOGGLE_TODO:
          return state.map((todo) =>
            todo.id === action.id ? { ...todo, done: !todo.done } : todo
          );
        default:
          return state;
      }
    }

     

    modules/index.js

    import { combineReducers } from 'redux';
    import counter from './counter';
    import todos from './todos';
    
    const rootReducer = combineReducers({
      counter,
      todos,
    });
    
    export default rootReducer;

     

    필요한 리듀서를 c.com에서 가져다 쓰기 위해서는 useSelector 훅을 사용해야 하는데요.

    이 훅은 useDispatch와 함께 'react-redux' 패키지에 있습니다.

     

    useSelector을 이용해서 상태를 가져올 때 다음처럼 합니다.

    import React, { useCallback } from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import Todos from '../component/Todos';
    import { addTodo, toggleTodo } from '../modules/todos';
    
    function TodosContainer() {
      const todos = useSelector((state) => state.todos);
      const dispatch = useDispatch();
    
      const onCreate = useCallback((text) => dispatch(addTodo(text)), [dispatch]);
      const onToggle = useCallback((id) => dispatch(toggleTodo(id)), [dispatch]);
    
      return <Todos todos={todos} onCreate={onCreate} onToggle={onToggle} />;
    }
    
    export default TodosContainer;

    state에 지금 상태가 자동으로 들어오는데, 이것이 가능한 이유는 컴포넌트의 최상단에서

    root reducer를 인수로 하여 store를 만들고 이를 내려주기 때문입니다.

     

    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { Provider } from 'react-redux';
    import { legacy_createStore as createStore } from 'redux';
    import rootReducer from './modules';
    import { composeWithDevTools } from 'redux-devtools-extension';
    
    const store = createStore(rootReducer, composeWithDevTools());
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <Provider store={store}>
        <React.StrictMode>
          <App />
        </React.StrictMode>
      </Provider>
    );
    
    // If you want to start measuring performance in your app, pass a function
    // to log results (for example: reportWebVitals(console.log))
    // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
    reportWebVitals();

     

    다시 useSelector 훅으로 돌아가볼게요.

    counter와 todos은 각각 리듀서 이름인데

    각 리듀서의 스테이트를 나타내는 이름이기도 합니다.

    스토어 생성 후에 스테이트를 출력해보면 다음처럼 나와요.

    useSelector(state=>state.todos)는 그렇게 나온 겁니다.

    댓글

Designed by Tistory.