Javascript application들의 state를 관리하는 방법
//index.html
<body>
<button id="add">Add</button>
<span></span>
<button id="minus">Minus</button>
</body>
//index.js
const add = document.getElementById("add");
const minus = document.getElementById("minus");
const number = document.querySelector("span");
let count = 0;
number.innerText = count;
const handleAdd = () => {
count = count + 1;
};
const handleMinus = () => {
count = count - 1;
};
add.addEventListener("click",handleAdd);
minus.addEventListener("click",handleMinus);
이렇게만 하면 count값은 변화하지만 화면에는 바뀐 값이 출력되지 않고 0으로만 뜬다
const updateText = () => {
number.innerText = count;
};
const handleAdd = () => {
count = count + 1;
updateText();
};
const handleMinus = () => {
count = count - 1;
updateText();
};
update 함수를 추가하고 다른 함수들을 실행할 때마다 같이 실행시켜주면 화면에도 바뀐 값이 출력된다
yarn add redux
Store : data(state)를 저장하는 곳
CreateStore : reducer를 요구
Reducer : data를 modify 해주는 함수
-> reducer가 return하는 것이 application에 있는 data가 됨
import { createStore } from 'redux';
const reducer = () => {};
const store = createStore(reducer);
store을 만들면 reducer을 만들라는 요청 옴
오직 reducer만 data를 modify 가능
const reducer = (state = 0) => {
return state;
};
console.log(store.getState());
state를 0으로 initializing
→ action을 통해 state를 modify
redux에서 function을 부를 때 쓰는 두번째 parameter로 reducer와 소통하기 위한 방법
const countStore = createStore(reducer);
countStore.dispatch({type:"ADD"});
Reducer에게 Action을 보내는 방법
💡 store.dispatch({key: value});
dispatch
: reducer에게 message 보내기
const reducer = (count = 0, action) => { //count = 0 : 현재 state
if (action.type === 'ADD') {
return count + 1;
}
else if (action.type === 'MINUS') {
return count - 1;
}
else
return count;
};
const countStore = createStore(reducer);
countStore.dispatch({type:"ADD"});
countStore.dispatch({type:"ADD"});
countStore.dispatch({type:"MINUS"});
current State : 0 → 1 → 2 →1 이런 식으로 변화
const handleAdd = () => {
countStore.dispatch({type:"ADD"})
}
const handleMinus = () => {
countStore.dispatch({type:"MINUS"})
}
add.addEventListener('click', handleAdd);
minus.addEventListener('click', handleMinus);
아까처럼 handleAdd, handleMinus 함수 만들어서 연결하기
→ ❗️ 화면 출력되지 않는 현상 발생
store 안에 있는 변화 감지
💡 store.subscribe(func);
store안의 변화를 감지하면 func 실행
const countStore = createStore(reducer);
const onChange = () => {
number.innerText = countStore.getState();
}
countStore.subscribe(onChange);
countStore의 변화를 감지해서 onChange를 실행 → 화면 출력
const reducer = (count = 0, action) => {
if (action.type === 'ADD') {
return count + 1;
}
else if (action.type === 'MINUS') {
return count - 1;
}
else
return count;
};
리덕스에서는 if-else보다 switch를 자주 사용한다?
const reducer = (count = 0, action) => {
switch(action.type){
case "ADD":
return count + 1;
case "MINUS":
return count - 1;
default:
return count;
}
};
string을 바로 쓰는 대신 const variable로 선언해서 사용하기 -> 에러 발견 용이
const ADD = "ADD";
const MINUS = "MINUS";
case ADD:
return count + 1;
case MINUS:
return count - 1;
배열을 만들어 추가하고 삭제하고 하는 것 불편 ! → redux 사용
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);
counter 때와 비슷하게 구현
💡 state mutate(변형) 금지
대신 new state objects를 리턴
→ 금지❌
state는 single source of truth이며, read-only이다
🌟 store을 수정할 수 있는 유일한 방법은 action을 보내는 방법뿐이다.
const reducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [...state, { text: action.text, id: Date.now() }];
case DELETE_TODO:
return [];
default:
return state;
}
};
const store = createStore(reducer);
store.subscribe(() => console.log(store.getState()));
return [...state, { text: action.text, id: Date.now() }];
→ ES6 문법 : …
array의 이전 컨텐츠에 새로운 object 합쳐서 새 array 만들기
filter() : 조건을 만족하는 element들로 새로운 array를 만든다
state.filter(toDo => toDo.id !== action.id);
toDo 안에 있는 element와 다른 id를 가진, 즉 선택되지 않은 element들로만 새로운 array 생성
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 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 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 dispatchAddToDo = text => {
store.dispatch(addToDo(text));
}
const dispatchDeleteToDo = (e) => {
const id = parseInt(e.target.parentNode.id);
store.dispatch(deleteToDo(id));
};
const onSubmit = (e) => {
e.preventDefault();
const toDo = input.value;
input.value = '';
dispatchAddToDo(toDo);
};
form.addEventListener('submit', onSubmit);