[Redux] redux 중급 - 문자열 입력/삭제 및 화면에 구현하기
1. Redux를 활용한 문자열 입력/삭제 및 화면 구현
문자열을 입력받아 화면에 나타나도록 하는 logic을 구현해본다.
2. 기본 구조 구성하기
기본적인 store, reducer 등 redux 구조 및 HTML을 구성한다.
2-1. HTML template 구성
화면을 먼저 구성하고, 각 tag 속성을 정의하여 준다.
<body>
<h1>To Do LIST</h1>
<form>
<input type="text" placeholder="Write to do" />
<button>Add</button>
</form>
<ul></ul>
</body>
- form tag 안에 input / button을 넣는다.
- input tag를 통해 문자열을 입력받고, button을 통해 이벤트를 발생시킨다.
※ form tag 전부를 addEventListener(submit) 발생시키도록 구성하여, 문자열입력과 버튼클릭 모두 이벤트 발생 및 서로 연결할 수 있도록 구성한다.
2-2. 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)
위 코드와 같이 form , input, ul tag를 document.querySelector를 통해 연결해준다.
이후 store를 생성하고, 기본적인 reducer 구조를 만들어준다.
action에 대한 분기처리는 switch로 구성하여 준다.
2-3. addEventListener (Event 설정 및 logic 연결)
Event를 연결해주고, action과 입력받은 문자열(text)를 모두 dispatch를 통해 전달하기
const onSubmit = e => {
e.preventDefault()
const toDO = input.value
input.value = ""
store.dispatch({type : "ADD_ToDo", text : toDO})
}
form.addEventListener("submit", onSubmit)
form 내부에서 일어나는 모든 이벤트(여기서는 ADD버튼을 클릭하였을 때의 submit 이벤트)에 대해 이벤트 설정을 해주고, onSubmit 함수를 호출하여 해당 logic을 작동한다.
onSubmit를 통해 action을 disptach한다.
- ADD_ToDO type을 전달하여 ADD_TODO 분기처리를 해준다(입력받은 문자열 추가).
- 이때 입력받은 문자열은 dispatch와 함께 전달되며, input.value를 통해 전달 및 toDO 변수에 최종 저장된다.
- onSubmit은 기본적으로 하위 tag들의 이벤트들(특히 onClick)을 감지하고, 이에 따른 하위 tag들의 value 값들을 확보할 수 있는 hooks의 일종이다.
※ prventDefault는 브라우저에서 이벤트 실행 시 자체적으로 동작하는 이벤트들을 강제적으로 막아주는 기능이다.
※ dispatch를 통해 action 뿐만 아니라 다른 여러 객체나 변수들도 전달할 수 있음을 기억한다.
2-4. action을 log 출력하여 출력 확인해보기
console.log(action)
전달받은 action을 log 출력해서 어떤 인자들이 출력되는지 확인해본다.
dispatch 받은 action 인자는 type과 text 모두 전달받았다.
특히 text는 input.value, 즉 입력받은 문자열이 그대로 전달받았음을 알 수 있다.
이를 활용하여 문자열을 화면에 구현하거나 삭제하는 기능을 추가 구현해본다.
3. state 변화를 반영하고 이를 subscribe 및 화면구현
변화한 state를 subscribe를 통해 화면에 구현한다(log 출력).
3-1. subscribe
dispatch를 통해 text 객체(변화 state) 전달하고, 이를 subscribe를 통해 연결한다.
const reducer = (state = [], action) => {
console.log(action)
switch(action.type){
case ADD_TODO : return [...state, { text : action.text } ]
case DELETE_TODO : return []
default : return state
}
}
새로운 객체를 반환할때 기존 state(...state)와 새로운 state 객체(text : action.text)를 반영하여 return 해준다.
3-2. log 확인
store.subscribe(() => console.log(store.getState()))
위 reducer logic을 통해 변화한 state를 받아오면, 이를 subscribe해서 logic을 출력해본다.
기존 state인 공배열 상태에서 변화한 객체들이 배열(state)에 누적되는 것을 살펴볼 수 있다.
- state 배열에 추가한 객체들이 누적된다.
- 누적된 객체들은 배열의 한 인덱스 공간을 차지(=인덱싱)하면서 저장된다.
- state의 getState, 즉 현재 state는 누적되어있는 객체들이 저장된 배열 상태이다.
3-3. state 상태반영을 통해 화면에 나타내기
전달받은 text를 새로운 tag를 생성하면서 해당 innerText에 생성하고,
새 tag의 id(unique 인자)에 할당할 id값을 dispatch 항목에 추가해준다.
const reducer = (state = [], action) => {
console.log(action)
switch(action.type){
case ADD_TODO : return [...state, { text : action.text, id : action.id } ]
case DELETE_TODO : return []
default : return state
}
}
const onSubmit = e => {
e.preventDefault()
const toDO = input.value
input.value = ""
store.dispatch({type : "ADD_TODO", text : toDO, id: Date.now()})
}
action 인자를 전달할 때, dispatch를 통해 text와 id 값을 전달한다.
※ 이때 id값은 Date.now를 통해 생성해주었다.
const paintTextToView = () => {
const lists = store.getState()
ul.innerHTML = ""
lists.forEach(listItem => {
const li = document.createElement("li")
li.id = listItem.id
li.innerText = listItem.text
ul.appendChild(li)
})
}
store.subscribe(paintTextToView)
전달받은 객체(text, id 값)을 그대로 활용하여, li tag를 생성하면서(document.createElement("li") 화면에 입력받은 text가 나타나도록 구성한다.
위 logic의 흐름은 아래와 같이 작동한다.
- add버튼을 누르면 submit 이벤트가 작동하면서 subscribe를 통해 paintTexttoView 함수가 실행된다.
- getState를 통해 변화한 상태값을 받아온다(action.text가 누적된 state 배열).
- 해당 상태값의 요소들을 loop하면서 li tag를 생성하고 화면에 구현한다(innerText).
- 이 생성한 li 속성들을 ul tag 하위에 종속시켜준다(appendChild).
※ 받은 객체를 그대로 loop(forEach)할 경우 누적된 배열상태를 그대로 복사하므로, 최근에 반영한 요소 값만 온전히 생성하도록 기존의 innerHTML 속성들을 공배열화.
3-4. 구현된 화면 확인
logic이 정상적으로 작동하는지 확인해본다.
3-5. (참조) reducer return 순서에 따른 구현 화면
return 하는 객체의 순서를 바꾸면 화면에 구현되는 모습도 바꿀 수 있다.
case ADD_TODO : return [{ text : action.text, id : action.id }, ...state, ]
위 코드처럼 return 하는 배열의 모습을 새로 생성된 객체 - 기존의 state 배열 순으로 하면, 최종적으로 getState를 통해 전달받는 구조도 새로운 객체 - 기존 stae({...state}) 순이므로 해당 순서 그대로 화면에 구현한다.
return할 때는 모든 배열요소를 전달해주므로, 순서와 함께 ul.innerHTML= ""을 선행해야 변화 요소만 화면에 구현해줄 수 있다는 것을 기억하자.
4. 화면에 구현된 문자열 삭제하기
화면에 문자열이 생성될 때 마다 DELETE 버튼을 생성하고,
해당 버튼을 누르면 삭제하는 logic을 구성한다.
4-1. 문자열 생성하면서 button 생성
createElement button도 같이 생성해준다.
lists.forEach(listItem => {
const li = document.createElement("li")
const btn = document.createElement("button")
li.id = listItem.id
li.innerText = listItem.text
ul.appendChild(li)
btn.innerText = "DELETE the list"
li.appendChild(btn)
})
subscribe 하면서 문자열 생성과 동시에, button tag도 같이 생성해준다.
이 버튼 tag는 문자열과 마찬가지로 상위 tag를 li로 하도록 구성해준다.
4-2. Delete button과 삭제 항목의 id 연결
Delete button과, Delete button이 생성된 상위 tag인 li의 id 값을 연결한다.
btn.innerText = "DELETE the list"
btn.addEventListener("click", (e) => {console.log(e.target.parentNode.id)})
li.appendChild(btn)
click 이벤트 실행 시
- e.target을 통해 event가 실행되는 tag 속성을 받아올 수 있다.
- e.target.parentNode.id를 통해 해당 tag가 종속된 부모 tag인 li tag id 값을 확보할 수 있다.
- 확보한 id 값을 이후 삭제하는 logic을 구성하도록 설정한다.
4-3. 삭제하는 action을 dispatch 및 reducer 내부 logic 구성
이벤트 발생 시 action을 dispatch 해주며 이때 action을 통해 id값을 전달한다.
실질적으로 해당 id 값을 삭제하는 logic은 reducer 내부에서 구성해준다.
btn.innerText = "DELETE the list"
btn.addEventListener("click", (e) => {
const id = e.target.parentNode.id
store.dispatch({ type : "DELETE_TODO", id })
})
li.appendChild(btn)
DELETE 버튼을 누르면 click 이벤트가 발생하며, 삭제할 요소의 id 값을 action dispatch를 통해 전달하는 구조를 구성해준다.
또한 DELETER 버튼을 눌렀을 때, reducer 내부에서 해당하는 logic을 동작할 수 있도록 reducer 에서 정의해준 삭제 type을 그대로 객체화하여( {type : "DELETE_TODO"} ) 전달해준다.
const reducer = (state = [], action) => {
console.log(action)
switch(action.type){
case ADD_TODO : return [{ text : action.text, id : action.id }, ...state, ]
case DELETE_TODO : return state.filter
default : return state
}
}
reducer 내부는 해당 action을 dispatch 받고, 전달받은 type과 id를 통해 어떤 삭제 logic을 구현해야 하는지 생각해본다.
4-4. array.filter를 통한 새로운 객체 반환 구조(삭제 logic) 구성
삭제 logic을 구성하는 핵심 요소는 아래 두가지 요소이다.
- dispatch를 통해 전달받은 action의 type
- dispatch를 통해 전달받은 action의 id
이 요소를 활용하여 state를 직접 mutation하지 않고(push, attend 등),
새로운 객체 값(배열)을 반환해주는 filter 함수를 이용한다.
const reducer = (state = [], action) => {
console.log(action)
switch(action.type){
case ADD_TODO : return [{ text : action.text, id : action.id }, ...state]
case DELETE_TODO : return state.filter(deletedList => deletedList.id !== Number(action.id))
default : return state
}
}
위 코드처럼 삭제 type이 전달되어, 삭제 logic을 구현하게 되며
- state.filter를 통해 현재 나타난 배열 상태에서 조건을 만족하는 배열만 return하고 이를 화면에 구현하게 된다.
- forEach는 배열의 요소를 모두 loop하고, 우리가 활용할 조건은 id값이므로 이를 염두하면서 조건을 처리한다.
- 조건을 만족하는 요소만 return하여 화면에 구현하면 되는데, id값이 다른 요소들만 state에 반영한다면 id값을 제외한 나머지 요소들이 state에 나타날 것이다.
- 이 state는 id값을 제외하므로, 삭제된 효과를 가진다.
4-5. 삭제 logic 확인
logic이 정상적으로 작동하는지 확인한다.
(※ 화면 상에서는 second 항목이 삭제된 모습)
5. 참조링크
array 관련 javascript method 공식문서(filter 참조)
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
event.preventDefault
https://pa-pico.tistory.com/20
Author And Source
이 문제에 관하여([Redux] redux 중급 - 문자열 입력/삭제 및 화면에 구현하기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@gyrbs22/Redux-redux-중급-문자열-입력삭제-및-화면에-구현하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)