Redux를 사용한 간단한 todo list를 만들기 실습을 진행했다.
Module 생성, Presentational component생성, Container component생성 순서이며, component는 꼭 나눌 필요가 있는건 아니다.
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 = {};
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;
}
}
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
//! 한프로젝트에 리듀서가 여러개일경우 한 리듀서로 합쳐사용한다.
//! 합쳐진 리듀서를 루트 리듀서라고 부른다.
// combineReducers 함수를 사용하여 리듀서를 합침.
const rootReducer = combineReducers({
counter,
todos,
});
export default rootReducer;
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;
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;
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();