store안의 state ⇒ state에 직접 변경하면 안된다. 어떤 것을 통해 변경을 해야한다.
state를 만들면서 reducer
를 만들어 공급해주어야 한다.
function reducer(oldState, action) {
// ...
}
var store = Redux.createStore(reducer) // 새로운 store생성 인자로 reducer를 준다.
render는 store바깥에 있다. ⇒ 우리의 코드. UI를 만들어주는 우리가 짜는 코드.
state에 직접 접속하는게 불가능하기 때문에 앞단에 dispatch
, subscribe
, getState
함수가 있다.
function render() {
var state = store.getState(); // store에서 state를 가져온다.
// ...
document.querySelector('#app').innerHTML = `
<h1>WEB<h1>
...
`
render는 현재 state를 반영하여 보여준다.
state가 변경될때마다 render가 바뀌면 좋을것이다. 이 때 사용하는 것이 subscribe
이다.
store.subscibe(render);
// 이렇게 store에 render를 subscribe하면 state가 변경될때마다 render가 갱신될것이다.
<form onsubmit = "
// ...
store.dispatch({type:'create;, payload:{title:title, desc:desc}});
">
// type안의 것이 action이고 이것을 dispatch에 전달
dispatch
가 하는일은?
reducer
를 호출해서 state값을 바꾼다.subscribe
를 이용해서 render함수를 호출한다. 그러면 화면이 갱신된다.dispatch
는 reducer
를 호출해서 현재의 state값과 action데이터를 전달한다.
function reducer(state, action){
if(action.type === 'create'){
...
...
return Object.assign({}, state, {
...
...
})
// return해주는 값이 새로운 state가 된다.
dispatch는 state를 입력값으로 받고 action을 참조해서 새로운 state값을 만들어서 return하는 state가공자이다.
이렇게 state값이 변경되면 render가 다시 일어난다. dispatch가 subscribe를 모두 호출하면 render가 일어난다. (getState를 통해서)
store의 state에 직접 접근이 안되기 때문에getState를 통해 state를 가져오고 dispatch를 통해 값을 변경하고 subscribe를 통해 값이 변경됐을때 구동될 함수를 등록한다. 또 하나의 핵심은 reducer를 통해 state값을 변경한다.
npm install --save redux
💡
redux cdn
으로 직접 설치하지 않고 copy해서 사용할 수도 있다. copy한 것을 script에 추가하면 된다.
리덕스를 이용한다는 것은 결국 store를 만들어서 store의 state를 바꾸는 것이다.
function reducer(staet, action){ // --- (1)
if(state === undefined){ // --- (2)
return {color: 'yellow'} // --- (3)
}
}
var store = Redux.createStore(reducer) // store 만들고, reducer 주입
console.log(store.getState()) // state값 가져오기 {color: yellow} 출력(초기값)
(1) 인자로 기존 staet값과 action을 받는다.
(2)state === undefined
는 state의 초기값이 아직 setting이 되기 전, 최초의 초기화 단계라는 의미이다.
(3) 여기서 리턴하는 것은 초기 state값을 의미한다. state 초기값은 바로 여기서 리턴하는 것이라는 것!
undefined
일때 리턴하는 값은 초기 state값이라 생각하면 된다. 초기화를 위해 최초로 실행되는 reducer에 대한 호출이기 때문에 우리가 원하는 초기값을 리턴해주면 이 리덕스의 store에는 그 초기값이 지정이 된다. 그래서 그렇게 저장된 state값을 getState를 통해 가져오고 변수에 할당해 우리의 컴포넌트에 할당해주면 그 초기 state를 사용할 수 있다.state값을 변경시키기 위해서는 어떻게 해야할까?
store.dispatch({type: 'CHANGE_COLOR', color: 'red'})
// 이 코드에 의해 store의 state값이 color를 red로 바꾸고 싶다면?
store에 dispatch를 호출하게 되면은 dispatch는 우리가 store를 생성할 때 제공한 reducer라고 하는 함수를 호출하도록 약속되어있다. 그때 이전의 state값을 전달한 action을 인자로 준다.
console.log(state, action)
// 처음엔 undefined, {type : "@@redux/어쩌고"} 이 출력
// 어떠한 버튼을 클릭한다면 {color: 'yellow'} {type: 'CHANGE_COLOR', color: 'red'} 이 출력
function reducer(staet, action){
if(state === undefined){
return {color: 'yellow'}
}
return {color: 'red'} // 어떠한 이벤트를 발생했을때 state값을 이것으로 바꿔준다.
}
// 이렇게도 사용할 수 있다. 하지만 좋지 않은 방법이다.
function reducer(staet, action){
if(state === undefined){
return {color: 'yellow'}
}
if(action.type === 'CHANGE_COLOR'){
state.color = 'red' // state의 color를 'red'로 변경
}
return state // 그것을 return
}
위처럼 state를 color를 변경하고 그것을 리턴하는 것이 아닌 이 state를 복제하고 복제된 복사본을 변경해서 return 하는것이 좋다. 이유는 바로 불변성 때문이다.
복제할 때 Object.assign()
을 사용한다. 첫 번째 인자로는 빈 객체, 두 번째 인자로는 빈객체의 복제할 속성을 가진 객체를 주면 된다.
Object.assign({}, {name:'daseul'}, {city: 'seoul'})
이렇게 작성하면 여기 있는 객체의 name속성이 'daseul'이라는 값을 assign
이 복제해준다. 그 뒤의 값도 복사가 된다. 즉 첫번째 인자에 두번째 인자를 복사하고 그 결과에 다시 세 번째 인자를 복사하는 것이다.
이렇게 되면 위의 코드는 {name:'daseul', city:'seoul'}
이라는 새로운 객체가 복사된다. 그것이 assign
이다. 첫 번째 인자는 무조건 빈 객체를 주어야 한다. 왜냐하면 Object.assign()
의 리턴값은 첫 번째 인자이기 때문이다.
// Object.assign()을 이용한 올바른 방법
function reducer(staet, action){
if(state === undefined){
return {color: 'yellow'}
}
if(action.type === 'CHANGE_COLOR'){
newState = Object.assign({}, state, { color: 'red' })
// state의 값이 첫번째 인자인 빈 객체에 복제되어 리턴되고 세번째 인자가 덮어씌워진다.
}
return newState // 복제된 값 return!
}
이전에는 원본인 state를 직접 변경했는데 이제는 state를 복제하고 그 복제한 것의 color를 red로 준것을 리턴하게 된다. 그러면 이 reducer가 실행될 때마다 리턴되는 값이 새로운 state값이 되는데 각각의 state값이 완전히 독립되어 복제된 값들이 리턴된다.
위에서 reducer를 통해 state값을 변경해주었다. 그러면 이 변경된 state값을 우리의 UI에 반영해주려면 어떻게 해야할까?
var store = Redux.createStore(reducer)
function red() {
var state = store.getState()
document.querySelector('#red').innerHTML = `
<div class="container" id="component_red"
style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="fire">
</div>
`
}
red()
red()
라는 UI를 바꿔주는 함수는 state값에 따라서 color를 바꿔서 UI를 변경해주는 함수이기 때문에 언제든지 호출할 수 있다. 일단 위의 코드에서 보듯 red()
를 최초 한번은 호출을 해주었는데 그 이후에 state가 바뀔때마다 그러니까 dispatch를 호출할때마다 red함수를 호출하게 하려면 어떻게 해야할까?
⇒ subscribe에 render를 등록하면 된다. 그러면 dispatch가 state값을 바꾸고 난 다음에 이것을 호출하도록 약속되어 있기 때문이다.
var store = Redux.createStore(reducer)
function red() {
var state = store.getState()
document.querySelector('#red').innerHTML = `
<div class="container" id="component_red"
style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="fire">
</div>
`
}
red()
store.subscribe(red) // !!!!
store의 subscibe함수에 red함수를 인자로 넣어 호출해준다. 그러면 state가 바뀔때마다 red함수가 호출된다.
이렇게 Redux를 사용하게 되면 상태를 중앙집중적
으로 관리할 수 있다. 각각의 부품들은 상태가 바뀌었을때 action을 store에 dispatch해주고 자신이 어떻게 변화되어야 하는지에 대한 코드를 작성하고 그것을 store에 subscribe하면 state가 바뀔때마다 통보를 받기 때문에 그때마다 자신의 모양을 바꿔줄수있는것이다.
- reducer 함수가 하는 역할은 store의 state값을 변경해준다. 어떻게? action의 값과 이전의 state값을 이용해서 새로운 state 값을 리턴해주면 그 리턴된 값이 새로운 state값이 된다.
그리고 리턴하는 값은 원본을 바꾸는 것이 아니라 이전에 있었던 값을 복제한 결과를 리턴해야지만 우리가 리덕스를 사용하는 여러가지 효용들을 최대한으로 활용할 수 있다 !- 원래 서로의 컴포넌트들은 자신의 상태가 변경되었을때, 자신에게 의존하고 있는 다른 컴포넌트들에게 그것을 직접 알려줘야했다. 하지만 redux를 사용하게 되면 모든 컴포넌트들이 rendering될 때 필요한 데이터를 담고 있는 stae를 만들고, 만약 action을 전달해 reducer를 통해 staet가 변경되면 모든 컴포넌트들에게 state가 변경되었음을 알리고 sate가 변경됨에 따라 subscribe에 포함되어 있는 함수들이 실행되면서 컴포넌트들이 재렌더링되기 때문에 컴포넌트들간의 의존성이 낮아지고 컴포넌트들은 각자 자신의 상태에만 집중할 수 있다.