❗️해당 게시물은 생활코딩-Redux강의를 수강하고 작성한 게시물입니다.
정보를 저장하는 곳이다.
실제 정보가 저장된다.
store에 있는 state를 직접 접근하는 것이 금지되어 있다.
이전의 state와 action을 받아서 다음의 state값을 리턴해준다.
reducer 요약 형태)
function reducer(oldState, action){
//....
}
var store = Redux.createStore(reducer); //store 생성
reducer 형태)
function reducer(state,action){
if(action.type === 'create'){
let newContents = oldState.contents.concat();
let newMaxId = oldState.maxId + 1;
newContents.push({id:newMaxId, title: action.payload});
return Object.assgin({}, state,{
contents : newContents,
maxId : newMaxId,
moode : 'read',
selectedId : newMaxId
})
}
}
UI를 만들어 주는 역할
리덕스와는 상관없는 로직이다.
render 형태)
function render(){
var state = store.getState(); //state 값을 가져오는 것이다.
document.querySelector('#app').innerHTML = `<h1>WEB</h1>` ....
}
store에 state 값이 변경될 때 마다 render함수를 호출할 수 있다면 얼마나 좋을까? render함수만 잘 만들면 알아서 state값이 변경될 때마다 UI가 갱신되지 않을까?
그럴 때 사용하는 게 subscribe이다.
dispatch가 subscribe에 등록되어 있는 구독자들을 다 호출한다.
그러면 render가 호출되면서 getstate호출 → state저장
store.subscribe(render);
dispatch는 두가지 일을 한다.
1. reducer호출 - dispatch가 reducer를 호출할 때 두개의 값을 전달한다. 첫번째 현재 stae값 두번째는 액션을 전달한다.
<form onSubmit = "store.dispatch({type : 'create', payload : {title:title, desc : desc}})";
- submit을 눌렀을 때, 객체 하나를 전송한다. 이때 이 객체를 action이라고 한다.
- 이 action은 dispatch에 전달된다.
- dispatch는 2가지 일을 한다.
- reducer를 호출해서, state 값을 바꾼다.
- reducer에 현재의 state 값과, action을 값이 주어진다.
- reducer의 return 하는 객체는 state의 새로운 값이 된다.
- reducer는 state를 입력 값으로 가지고, action을 참조해서, 새로운 state 값을 만들어서, return 해주는 state를 가공해주는 가공자이다.
- 이후, subscribe를 이용해서, render를 호출한다.
- 새로운 state에 맞게 UI가 변한다.
스토어 생성한 코드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.1/redux.js" integrity="sha512-Olr8rkMYuxq3KpAPjYA/mAVYe7EIEP4RkhoAvD/qOrlauzE4CTvpQSg/cRX0/5Qreret4aobD0vg1xtjBqR7VA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<style>
.container {
border: 5px solid black;
padding: 1rem;
margin-bottom:1rem;
}
body{
margin:1rem;
}
</style>
<h1>Without redux</h1>
<div id="red"></div>
<script>
function reducer(state, action){
if(state === undefined){
return {color:'yellow'} //초기 state값
}//최초의 초기화단계
}
let store = Redux.createStore(reducer);
console.log(store.getState())
function red(){
let 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();
</script>
</body>
</html>
state값을 변경시키려면 action을 만들고 dispatch에게 제출하면 dispatch는 reducer를 호출한다. 그 때에 이전 state값, action값 동시에 줌 reducer함수가 그걸 분석해서 state값 최종적으로 리턴
store을 처음 생성하면 최초 1회 action과는 상관없이 호출되는데 그 때에 state값은 undefined 이다. 그렇기 때문에 초기값을 setting 할 기회이다.
초기 setting 형태)
function reducer(state, action){
if(state === undefined){
return{
}
}
}
아래에 코드에서 state변경하는 잘못된 방법을 살펴 보겠다.
store.js
function reducer(state, action){
console.log(state, action);
if(state === undefined){
return { color: "yellow" }
}
if(action.type === "CHANGE_COLOR"){
state.color = "red";
}
}
액션보내는컴포넌트.js
let state = store.getState();
store.dispatch({type:'CHANGE_COLOR', color:'red'})
원본 state를 직접 다루게 된다면 redo, undo, hot reloading 혜택을 못누리게 된다.
해결방법
state값을 변경해서 리턴하지말고 해당 state값을 복제해서 복사본을 변경해서 게를 리턴하는게 좋다. 그래야지만 redo,undo를 할 수 있고 우리 어플리케이션을 예측 가능하게 동작할 수 있다 - ❗️Immuatbility 생활코딩 참고
store.js
function reducer(state, action){
console.log(state,action)
if(state === undefined){
return {color:'yellow'} //초기 state값
}//최초의 초기화단계 --최초 한번은 무조건 실행
let newState;
if(action.type === "CHANGE_COLOR"){
newState = Object.assign({},state,{color : 'red'})
}
//리듀서가 실행될 때 마다 리턴되는 값이 새로운 state값이 되는데 각각의 state값들이 서로 독립되어있다.(복제된 결과들이 리턴되어야 한다.)
return newState;
}
let store = Redux.createStore(reducer);
console.log(store.getState())
객체를 복사할 때는
Obect.assign()
Object.assign({}, {name : 'egoing'}, {city :'seoul'});
첫번째 인자에 두번째 인자를 복사하고 그 결과에 3번째 인자를 복사한다.
결과
{name : "egoing", city :"seoul"}
assign은 첫번째 인자로 반드시 빈 객체를 줘야한다.
→Obect.assgin 리턴값은 첫번째 인자이기 때문이다.
결론 : Reducer역할은 store에 state값을 변경해준다. action의 값과 이전의 state값을 이용해서 새로운 state값을 리턴해주면 그 리턴된 값이 그 리턴된 값이 새로운 state값이 된다. 그리고 return값 원본을 변경하는게 아니라 이전의 값을 복제한 결과를 return 해야 한다.
액션보내는컴포넌트.js
function red(){
let 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>
`;
}
store.subscribe(red); //이 코드를 작성해주면 된다.
red();
dispatch 를 할 때 마다 red()함수를 호출하게 만들려면 subscribe에 render를 등록해 놓으면 된다.
Redux가 없는 코드는 각각의 component(부품) 간의 의존성이 너무 높습니다.
예를 들어, 다른 component의 코드를 수정하거나 삭제하면(Red 삭제), 다른 component와(Blue, Green) 연결된 코드가 얽혀있어 오류가 발생합니다.
그러므로 component 추가, 수정, 삭제 시, 기존의 component를 모두 수정해야 합니다.
하지만, Redux를 통해 중앙 집중형 관리를 하면, 각각의 component는 action(상태가 바뀌었다는 것)을 store에 dispatch(통보)하면 됩니다.
이에 따른 자신의 변화를 작성 후 store에 subscribe(구독)하면, state가 바뀔 때마다 통보를 받기 때문에 다른 component와의 연결 없이 자신의 모양을 자유롭게 바꿀 수 있습니다.
즉, 수정해도 다른 부품들은 영향을 받지 않게되죠.
요약하자면, Redux를 통해 각 Module의 독립성을 보장받을 수 있습니다.
Redux는 모든 Application에 관리해야 되는 상태는 store에 다 보관되게 되어 있다.
store.js
function reducer(state, action){
console.log(state,action)
if(state === undefined){
return {color:'yellow'} //초기 state값
}//최초의 초기화단계 --최초 한번은 무조건 실행
let newState;
if(action.type === "CHANGE_COLOR"){
newState = Object.assign({},state, {color : action.color}) //state 복제
}//하드코딩이란?
console.log(action.type, action, state, newState);
return newState;
}
let store = Redux.createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
//Redux-dev-tools를 사용하기 위한 코드
)