[Redux] Action, Reducer, Store

홍인열·2022년 2월 14일
0
post-custom-banner

Redux를 사용한 간단한 todo list를 만들기 실습을 진행했다.
Module 생성, Presentational component생성, Container component생성 순서이며, component는 꼭 나눌 필요가 있는건 아니다.

1. Module 생성 { todos.js }

액션 선언

const ADD_TODO = 'todos/ADD_TODO';
const TOGGLE_TODD = 'todos/TOGGLE_TODD';
// 모듈이름을 같이 써주면 다른 모듈과 액션 이름이 중복되는 것을 방지 할수 있음.

액션 생성함수 선언

let nextId = 1; //데이터에서 사용할 고유 id (필요시)

export const addTodo = (text) => ({
  type: ADD_TODO, //액션 생성함수에서 type은 필수! 이후 데이터는 마음대로 추가할 수 있다.
  todo: {
    id: nextId++,
    text,
  },
});

export const toggleTodo = (id) => ({
  type: TOGGLE_TODD,
  id,
});

초기 상태 선언

// 보통 객체를 많이 사용하지만 꼭 객체일 필요가는 없다.
const initialState = {};

Reducer 선언

export default function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODO:
      return state.concat(action.todo); // 초기배열에 todo를 추가해준다. concat은 원본배열을 변화시키지않는다.
    case TOGGLE_TODD:
      return state.map((todo) =>
        todo.id === action.id //
          ? { ...todo, done: !todo.done }
          : todo
      );
    default:
      return state;
  }
}

combineReducer { index.js }

import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';

//! 한프로젝트에 리듀서가 여러개일경우 한 리듀서로 합쳐사용한다.
//! 합쳐진 리듀서를 루트 리듀서라고 부른다.
// combineReducers 함수를 사용하여 리듀서를 합침.

const rootReducer = combineReducers({
  counter,
  todos,
});

export default rootReducer;

2. Presentational Component <Todos />


import React, { useState } from 'react';

//component
const TodoItem = React.memo(({ todo, onToggle }) => { //React.memo() 추가 공부할 부분! 컴포넌트 최적화와 관련
  return (
    <li //
      style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
      onClick={() => onToggle(todo.id)}
    >
      {todo.text}
    </li>
  );
});

//component
const TodoList = React.memo(({ todos, onToggle }) => {
  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} onToggle={onToggle} />
      ))}
    </ul>
  );
});

//component
function Todos({ todos, onCreate, onToggle }) {
  const [text, setText] = useState('');
  const onChange = (e) => setText(e.target.value);
  const onSubmit = (e) => {
    e.preventDefault(); // submit evt 발생시 새로고침 방지, 없을경우 리스트가 추가되자마자 새로고침되면서 초기화된다.
    onCreate(text);
    setText('');
  };

  return (
    <div>
      <form onSubmit={onSubmit}>
        <input //
          value={text}
          placeholder="할 일을 입력하세요."
          onChange={onChange}
        />
        <button type="submit">등록</button>
      </form>
      <TodoList todos={todos} onToggle={onToggle} />
    </div>
  );
}

export default Todos;

3. Container Component <TodosContainer />

import React, { useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Todos from '../components/Todos';
import { addTodo, toggleTodo } from '../modules/todos';

//component
function TodoContainer() {
  // useSelector에서 꼭 객체를 반환 할 필요는 없다.
  const todos = useSelector((state) => state.todos);
  const dispatch = useDispatch();

  //dispatch를 통해 상태를 업데이트할 수 있다.
  const onCreate = (text) => dispatch(addTodo(text));
  const onToggle = useCallback((id) => dispatch(toggleTodo(id)), [dispatch]);
  //최적화를 위해 useCallback사용, 추가 공부 필요!!

  return <Todos todos={todos} onCreate={onCreate} onToggle={onToggle} />;
}

export default TodoContainer;

4. store생성 및 view

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { createStore } from 'redux';
import rootReducer from './modules'; //index.js가 default
import { Provider } from 'react-redux';
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(rootReducer, composeWithDevTools());
//composeWithDevTools() 는 크롬 개발자도구르 redux 상태 기록 확인을 위해 추가, 기능구현에 영향 없음.

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}> //Provider를 이용해 <App/> 을 감싸면 모든 컴포넌트에서 store에 접근 가능
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();
profile
함께 일하고싶은 개발자
post-custom-banner

0개의 댓글