-
[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)는 그렇게 나온 겁니다.
'개발 > React' 카테고리의 다른 글
[Proxy error] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema. - options.allowedHosts[0] should be a non-empty string. (0) 2022.07.11 redux-thunk react-router-dom v6 (2) 2022.07.11 [Redux] 아주 아주 간단한 리덕스 기본 개념 2 (0) 2022.07.08 [Redux] 아주 아주 간단한 리덕스 기본 개념 (0) 2022.07.08 React & Immer (0) 2022.06.08