이번에는 좀 더 심화학습으로 pure redux를 사용한 투두리스트를 만들어 봤음.
시작은 기존 javascript 를 이용하여 todo리스트를 만들어줌.
<index.html>
<h1>To Dos</h1>
<form>
<input type="'text" placeholder="write to do" />
<button>Add</button>
</form>
<ul></ul>
<index.js>
const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");
const createToDo = (toDo) => {
const li = document.createElement("li");
li.innerText = toDo;
ul.appendChild(li);
};
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = "";
createToDo(toDo);
};
form.addEventListener("submit", onSubmit);
역시나 이렇게 하면 우리 application은 data를 가지고 있지 않음
단순 html수정에 불과함. 데이터를 저장하는 것이 아님.
import {createStore} from 'redux'
const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");
const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";
const reducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [];
case DELETE_TODO:
return [];
default:
return state;
}
};
const store = createStore(reducer);
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = "";
store.dispatch({ type: ADD_TODO, text: toDo });
};
form.addEventListener("submit", onSubmit);
여기서 중요하게 생각할 것은 state.push(action.text)를 할 게 아니라 (mutation ❌❌) 새로운 값을 return 하는 것.
const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");
const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";
//아래에 있던 dispatch({type: ADD_TODO, text})를 따로 함수로 만듬
const addToDo =(text)=>{
return {
type: ADD_TODO,
text
}
}
const deleteToDo =(id)=> {
return {
type: DELETE_TODO,
id
}
}
const reducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
//return state.push(action.text) 이게 아니라 새로운 배열을 return 해야함.
//...state를 뒤로 보내서 array 가 보이는 방식을 수정할 수 있음.
return [{ text: action.text, id: Date.now() }, ...state]; //새로운 object로 array를 만든 것임.
case DELETE_TODO:
return [];
default:
return state;
}
};
const store = createStore(reducer);
store.subscribe(() => {
console.log(store.getState());
});
const dispatchAddToDo = (text) => {
store.dispatch(addTodo(text));
};
//parentNode를 알아야함. 왜냐면 삭제할 todo의 id가 필요하기 때문
const dispatchDeleteToDo = (e) => {
const id = e.target.parentNode.id;
store.dispatch(deleteToDo(id));
};
const paintToDos = () => {
const toDos = store.getState();
//이렇게 하면 계속 누적해서 리스트 쌓임. 아래 입력해줘야함.
ul.innerHTML = "";
toDos.forEach((toDo) => {
const li = document.createElement("li");
const btn = document.createElement("button");
btn.innerText = "DEL";
btn.addEventListener("click", dispatchDeleteToDo);
li.id = toDo.id;
li.innerText = toDo.text;
li.appendChild(btn);
ul.appendChild(li);
});
};
store.subscribe(paintToDos);
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = "";
addToDo(toDo);
};
form.addEventListener("submit", onSubmit);
여기까지하면 삭제 버튼을 눌렀을때 리스트가 전부 삭제 됨.
삭제 버튼을 눌렀을 때 mutation한 방법 (splice) 말고 ❌ 다른 방법을 생각해야하는데, 이 때filter 메소드
이용한다.
(공식문서에 보면 filter메소드에 대해 '테스트를 통과한 모든 element들로 새로운 array를 만든다고 써있음)
const reducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO :
return [{text: action.text, id: Date.now()}, ...state]
case DELETE_TODO :
return state.filter(toDo => toDo.id !== action.id)
default :
return state;
//문자열을 숫자로 바꿔주기 위해 parseInt 사용 (HTML로 받아오는 id는 string 타입일 것이기 때문)
const dispatchDeleteToDo = (e) => {
const id = parseInt(e.target.parentNode.id);
store.dispatch(deleteToDo(id));
};
````
### 3) 결과 코드
```javascript
const form = document.querySelector("form");
const input = document.querySelector("input");
const ul = document.querySelector("ul");
const ADD_TODO = "ADD_TODO";
const DELETE_TODO = "DELETE_TODO";
const addToDo = (text) => {
return {
type: ADD_TODO,
text,
};
};
const deleteToDo = (id) => {
return {
type: DELETE_TODO,
id,
};
};
const reducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [{ text: action.text, id: Date.now() }, ...state];
case DELETE_TODO:
return state.filter((toDo) => toDo.id !== action.id);
default:
return state;
}
};
const store = createStore(reducer);
store.subscribe(() => {
console.log(store.getState());
});
const dispatchAddToDo = (text) => {
store.dispatch(addToDo(text));
};
const dispatchDeleteToDo = (e) => {
const id = parseInt(e.target.parentNode.id);
store.dispatch(deleteToDo(id));
};
const paintToDos = () => {
const toDos = store.getState();
ul.innerHTML = "";
toDos.forEach((toDo) => {
const li = document.createElement("li");
const btn = document.createElement("button");
btn.innerText = "DEL";
btn.addEventListener("click", dispatchDeleteToDo);
li.id = toDo.id;
li.innerText = toDo.text;
li.appendChild(btn);
ul.appendChild(li);
});
};
store.subscribe(paintToDos);
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = "";
dispatchAddToDo(toDo);
};
form.addEventListener("submit", onSubmit);