Redux 를 이용하여 Todo App 을 만들어보았다.
핵심
CRUD 를 구현함에 있어서 title 과 content 는 하위 컴포넌트에서 useState 를 이용해 관리하고, 데이터만 action 으로 받아와서 처리한다. (input state 를 스토어에서 관리해보니 가독성도 떨어지고 코드만 복잡했었다..)
스토어에서 관리하는 건 새로운 객체를 생성할 때 주는 id 값과, 수정할 때 받아오는 updateId 값, 제목을 클릭했을 때 내용을 보이게 할 active 값, 할 일 객체가 담긴 배열 todos 가 전부이다.
/* src/modules/TodoModules.jsx */
import React, { useState, useEffect, useRef } from "react";
const padNumber = (num, length) => {
return String(num).padStart(length, "0");
}
const Time = () => {
let now = new Date();
const interval = useRef(null);
const [time, setTime] = useState({
hour: padNumber(now.getHours(),2),
min: padNumber(now.getMinutes(),2),
sec: padNumber(now.getSeconds(),2)
});//초기 state 설정
const {hour, min, sec} = time;
useEffect(()=> {
interval.current = setInterval(()=> {
now = new Date();
setTime({
hour: padNumber(now.getHours(),2),
min: padNumber(now.getMinutes(),2),
sec: padNumber(now.getSeconds(),2)
})
}, 1000)
return () => clearInterval(interval.current);
}, [])
return(
<div style={{
textAlign:"center",
fontSize: "50px",
marginBottom:"30px"
}}>
{hour} : {min} : {sec}
</div>
)
}
const TodoItem = ({todo,onDelete, inputModify, onToggle}) => { //{todo}= props.todo
return(
<div>
<p onClick={() => onToggle(todo.id)}>{todo.title}</p>
{todo.active ? <p>{todo.content}</p> : null}
<button onClick={() => {
inputModify(todo);
}}>MODIFY</button>
<button onClick={() => onDelete(todo.id)}>DELETE</button>
</div>
)
}
const TodoList = ({todos, onDelete, inputModify, onToggle}) => {//{todos} = props.todos
return(
<div>
<ul>
{todos.map((todo, index) => <TodoItem onDelete={onDelete} key={index} todo={todo} inputModify={inputModify} onToggle={onToggle}/>)}
</ul>
</div>
)
}
const Todos = ({onCreate, todos, onDelete, onModify, onUpdate, onToggle}) => {
const [inputs, setInput] = useState({
title: '',
content: ''
});
const {title, content} = inputs;
const inputModify = (todo) => {
//수정버튼을 눌렀을 때 input에 해당 글이 뜨고, 해당 글의 id를 dispatch한다
setInput({
title: todo.title,
content: todo.content
});
onModify(todo.id);
};
const inputChange = (e) => {
setInput({
...inputs,
[e.target.name]: e.target.value
})
};
const inputReset = () => {
setInput({
title:'',
content:''
})
}
return(
<div>
<Time/>
<input onChange={(e) => inputChange(e)} name="title" value={title} placeholder="제목을 입력하세요"/>
<input onChange={(e) => inputChange(e)} name="content" value={content} placeholder="내용을 입력하세요"/>
<button onClick={(e) => {
e.preventDefault();
onCreate(title,content);
inputReset();
}}>ADD</button>
<button onClick={(e) => {
e.preventDefault();
onUpdate(title,content);
}}>Update</button>
<TodoList todos={todos} onDelete={onDelete} inputModify={inputModify} onToggle={onToggle}/>
</div>
)
}
export default Todos;
/* src/containers/TodoContainer.jsx */
import React from "react";
import { useSelector, useDispatch } from "react-redux";//둘다 store의 내장 함수이다.
import Todos from "../components/Todos";
import { create, ddelete, modify, update, toggle } from '../modules/TodoModule';
function TodoContainer() {
const todos = useSelector(state => state.todos);
const dispatch = useDispatch();
const onCreate = (title, content) => dispatch(create(title, content));
const onDelete = (id) => dispatch(ddelete(id));
const onModify = (id) => dispatch(modify(id));
const onUpdate = (title,content) => dispatch(update(title,content));
const onToggle = (id) => dispatch(toggle(id));
return(
<Todos todos={todos} onCreate={onCreate} onDelete={onDelete} onModify={onModify} onUpdate={onUpdate} onToggle={onToggle}/>
)
}
export default TodoContainer;
/* src/components/Todos.jsx */
import React, { useState, useEffect, useRef } from "react";
const padNumber = (num, length) => {
return String(num).padStart(length, "0");
}
const Time = () => {
let now = new Date();
const interval = useRef(null);
const [time, setTime] = useState({
hour: padNumber(now.getHours(),2),
min: padNumber(now.getMinutes(),2),
sec: padNumber(now.getSeconds(),2)
});//초기 state 설정
const {hour, min, sec} = time;
useEffect(()=> {
interval.current = setInterval(()=> {
now = new Date();
setTime({
hour: padNumber(now.getHours(),2),
min: padNumber(now.getMinutes(),2),
sec: padNumber(now.getSeconds(),2)
})
}, 1000)
return () => clearInterval(interval.current);
}, [])
return(
<div style={{
textAlign:"center",
fontSize: "50px",
marginBottom:"30px"
}}>
{hour} : {min} : {sec}
</div>
)
}
const TodoItem = ({todo,onDelete, inputModify, onToggle}) => { //{todo}= props.todo
return(
<div>
<p onClick={() => onToggle(todo.id)}>{todo.title}</p>
{todo.active ? <p>{todo.content}</p> : null}
<button onClick={() => {
inputModify(todo);
}}>MODIFY</button>
<button onClick={() => onDelete(todo.id)}>DELETE</button>
</div>
)
}
const TodoList = ({todos, onDelete, inputModify, onToggle}) => {//{todos} = props.todos
return(
<div>
<ul>
{todos.map((todo, index) => <TodoItem onDelete={onDelete} key={index} todo={todo} inputModify={inputModify} onToggle={onToggle}/>)}
</ul>
</div>
)
}
const Todos = ({onCreate, todos, onDelete, onModify, onUpdate, onToggle}) => {
const [inputs, setInput] = useState({
title: '',
content: ''
});
const {title, content} = inputs;
const inputModify = (todo) => {
//수정버튼을 눌렀을 때 input에 해당 글이 뜨고, 해당 글의 id를 dispatch한다
setInput({
title: todo.title,
content: todo.content
});
onModify(todo.id);
};
const inputChange = (e) => {
setInput({
...inputs,
[e.target.name]: e.target.value
})
};
const inputReset = () => {
setInput({
title:'',
content:''
})
}
return(
<div>
<Time/>
<input onChange={(e) => inputChange(e)} name="title" value={title} placeholder="제목을 입력하세요"/>
<input onChange={(e) => inputChange(e)} name="content" value={content} placeholder="내용을 입력하세요"/>
<button onClick={(e) => {
e.preventDefault();
onCreate(title,content);
inputReset();
}}>ADD</button>
<button onClick={(e) => {
e.preventDefault();
onUpdate(title,content);
}}>Update</button>
<TodoList todos={todos} onDelete={onDelete} inputModify={inputModify} onToggle={onToggle}/>
</div>
)
}
export default Todos;
처음에는 input 값을 스토어에 관리하도록 구현했었는데 코드의 가독성, 복잡성만 늘어나고 에러가 발생했었다.
그래서 useState 를 이용해 하위 컴포넌트에서 input 값을 관리하니 코드가 깔끔해졌다. redux 를 필요한 부분에만 사용하는 것이 좋다고 공부할 때 배웠었는데, 이번 기회에 그것을 느낄 수 있었던 것 같다. 앞으로도 코드를 깔끔하게 구현하도록 노력해야겠다.
시계의 시간을 구현할때 그냥 사용하면 오전 5시면 5, 8분이면 8 이렇게 나오는데 padStart() 메서드를 사용하니 05시 08분 처럼 변경하기 매우 유용했다.