Redux가 가진 능력을 보고 꼭 Redux를 배우고싶다는 생각에 무작정 강의를 듣게되었고,
아직 나에게는 이해하기 어려웠던 내용인지라 여기에 정리하면서 다시 익혀보자는 생각이다.
정확한 내용이 아닐수도 있고, 유익한 내용이 아닐수도 있다는 점
우선 생활코딩에서도 이 이미지를 보고 Redux에 훅하게 되었는데 Without Redux, With Redux를 비교해보자. Component Initiating Change 즉 Component를 바꾸기 시작할때 다른 Component에 접근하는 모습에 확연한 차이가 생기는데 Redux를 쓸경우 Store라는 단일저장소를 이용하게되어 이는 부모자식간의 종속된 관계를 모두 깨뜨려버리는 엄청난 효과를 발휘한다고 표시되어있다.
복잡한 계층구조를 가지고 state를 쓰는 경험을 해보지는 못했지만, 이처럼 그 계층구조를 박살내고 Store라는 저장소를 만들어 state를 관리한다는 매혹적인 얘기를.. 배우지않고 지나갈 수 있겠는가 ?
사실 고백할게 하나 있는데 나는 Component - Store 구조를 간단하게 생각했지만 생각보다 그리 간단한 구조는 아니었다.
Component - Action - Reducer - Store - Component
당연하게도 이 구조는 다 이유가 있는데 상태를 쉽게 저장하고 예측 가능하게 관리를 해준다는데 사실 아직 와닿지 않는 이유이다.
리덕스의 철학
1. 앱의 모든 state는 한 장소에 저장. 즉, state를 갱신하려고 여기저기 찾아다닐 필요가 없다!
2. state는 오직 readable. 즉, 읽기 전용이고 액션을 통해서만 변경한다. 데이터 변경하는 유일한 방법이 액션!
3. 반!드!시! 마지막 state가 저장돼야 한다. state는 결코 수정, 변형 않는다는 말이다. 따라서 리듀서를 이용해 마지막 state값을 지정한다.
출저 : https://shiningjean.tistory.com/50 (짧게 정리를 잘해주셔서)
앞으로는 생활코딩의 코드 예로 알아보자.
Add Number의 input태그에 일정값을 입력하고 button으로 onClick할경우 Display Number에 onClick으로 전달된 값이 축적되며 표시되는 간단한 내용인데 그 구조를 보자.
App.js
class App extends Component{
state = {number:0}
render(){
return (
<div className="App">
<h1>Root</h1>
<AddNumberRoot></AddNumberRoot>
<DisplayNumberRoot></DisplayNumberRoot>
</div>
);
}
}
AddNumberRoot.js
import React, {Component} from 'react';
import AddNumber from '../containers/AddNumber';
export default class AddNumberRoot extends Component{
render(){
return(
<div>
<h1>Add Number Root</h1>
<AddNumber></AddNumber>
</div>
);
}
}
AddNumber.js
export default class AddNumber extends Component{
state = {size:1}
render(){
return(
<div>
<h1>Add Number</h1>
<input type="button" value="+" onClick={function(){
this.props.onClick(this.state.size);
}.bind(this)}></input>
<input type="text" value={this.state.size} onChange={function(e){
this.setState({size:Number(e.target.value)});
}.bind(this)}></input>
</div>
);
}
}
AddNumber.js(Container)
import AddNumber from "../components/AddNumber";
import {connect} from 'react-redux';
function mapDispatchToProps(dispatch){
return{
onClick:function(size){
dispatch({type:'INCREMENT', size:size});
}
}
}
export default connect(null, mapDispatchToProps)(AddNumber);
Container/AddNumber.js 가 없다고 생각하고(밑에서 Container/DisplayNumber.js와 같이 다루겠다) 다른코드들을 보면 어떤차이가 있을까?
우선 기존에 Component/AddNumber.js에서 action에 대한 코드를 쓸 때
<input type="button" value="+" onClick={function(){
this.props.onClick(this.state.size);
}.bind(this)}></input>
<input type="text" value={this.state.size} onChange={function(e){
this.setState({size:Number(e.target.value)});
}.bind(this)}></input>
이와같이 onClick이 일어나면 전해줄 매개변수(size),(e.target.value)를 정해주고
<AddNumber onClick={function(size){
this.props.onClick(size);
}.bind(this)}></AddNumber>
이처럼 태그를 직접쓸 때 받을 인자를 적어주는게 보통이지만 redux로인해
container화(AddNumberRoot.js파일에서 Addnumber.js를 import할때 경로를 from '../containers/AddNumber' 로 둔갑)
시켰기 때문에 맨위의 AddNumberRoot.js에 AddNumber.js Component를 쓸때
<AddNumber></AddNumber>
이와같이 깔끔하게 쓸 수 있다. 고작 깔끔하게 쓰기위해서 container를 따로 분리했을까 ?
container/AddNumber라는 빈껍데기에 본래 분리하기 전 Component/AddNumber에 담겨있는 store에 종속될 성질만을 빼서 넣어준다. 그러면 store는 state값을 관리하게되고 남은 Component/AddNumber은 재사용성(부품으로써의 가치를 가질 수 있게되었다)을 가지게 되었다.
그럼 Action을통해 전달되는 state값을 저장하는 store.js를 보자
store.js
import {createStore} from 'redux';
export default createStore(function(state, action){
if(state === undefined){
return {number:0}
}
if(action.type === 'INCREMENT'){
return {...state, number:state.number + action.size}
}
return state;
}, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
createStore()라는 함수안에 function(){}, window.__RE...(생략)
두개의 매개변수를 갖는데 뒤의 변수는 redux dev tool을 사용하기 위한 코드라고 보면된다
그리고 function의 내용은 state와 action을 매개변수로 받고 받은 state값으 undefined 즉 아직 초기값일때 {number:0}라는 객체를 DisplayNumber로 return한다고 이해했다. 그리고 다시
AddNumber.js(container) 참조
function mapDispatchToProps(dispatch){
return{
onClick:function(size){
dispatch({type:'INCREMENT', size:size});
}
}
}
if(AddNumber의 onClick으로 dispatch를 통한 type값이 'INCREMENT'로 변경되었다면)
if(action.type === 'INCREMENT') 라면 우리는 기존 number값에 action.size로 AddNumber에서 Store로 보내 저장된 size값을 더한값을 return하라고 나타단다.
// ...state는 데이터를 복제하는 방식 , 그리고 복제한 state값을 number:state.number + action.size로 변경하는 모습이다.
그리고 AddNumber를 통해 받은 값을 축적시키는 DisplayNumber의 코드를 보자
DisplayNumberRoot.js
import React, {Component} from "react";
import DisplayNumber from "../containers/DisplayNumber";
export default class DisplayNumberRoot extends Component{
render(){
return (
<div>
<h1>Display Number Root</h1>
<DisplayNumber unit="kg"></DisplayNumber>
</div>
)
}
}
DisplayNumber.js
export default class DisplayNumber extends Component {
render() {
return (
<div>
<h1>Display Number</h1>
<input type="text" value={this.props.number} readOnly></input>
{this.props.unit}
</div>
)
}
}
DisplayNumber.js(container)
import DisplayNumber from '../components/DisplayNumber';
import {connect} from 'react-redux';
function mapReduxStateToReactProps(state){
return {
number:state.number
}
}
export default connect(mapReduxStateToReactProps)
(DisplayNumber);
AddNumber을 통해 변경되는 state값을 store로 다시 그 값을 view Component인 DisplayNumber에 전달함으로써 표시되는 값이 변경되는 코드를 살펴봤는데 마지막으로 container에 mapReduxStateToReactProps, mapDispatchToProps 를 살펴보겠다.
이를 살펴보기 위해서는 DisplayNumber.js(container)가 react-redux가 없을때 코드를 먼저 보겠다.
import DisplayNumber from '../components/DisplayNumber';
import {connect} from 'react-redux';
function mapReduxStateToReactProps(state){
return {
number:state.number
}
}
export default connect(mapReduxStateToReactProps)
(DisplayNumber);
// export default class DisplayNumberRoot extends Component{
// state = {number:store.getState().number}
// constructor(props){
// super(props);
// store.subscribe(function(){
// this.setState({number:store.getState().number});
// }.bind(this));
// }
// render(){
// return <DisplayNumber number={this.state.number} unit={this.props.unit}></DisplayNumber>;
// }
// }
주석처리한 내용이 모두 react-redux가 없어 mapReduxStateToReactProps를 사용하지 못한다면 DisplayNumberRoot.js Component가 써야할 코드들인데 리를 mapReduxStateToReactProps을 통해 Redux의State를 React의Props로 변경해주는 map이라고 생각하면 편할 듯 하다.
아래는 mapDispatchToProps를 사용하지 못한다면
import DisplayNumber from '../components/DisplayNumber';
import {connect} from 'react-redux';
function mapReduxStateToReactProps(state){
return {
number:state.number
}
}
export default connect(mapReduxStateToReactProps)
(DisplayNumber);
// export default class DisplayNumberRoot extends Component{
// state = {number:store.getState().number}
// constructor(props){
// super(props);
// store.subscribe(function(){
// this.setState({number:store.getState().number});
// }.bind(this));
// }
// render(){
// return <DisplayNumber number={this.state.number} unit={this.props.unit}></DisplayNumber>;
// }
// }
이라는 내용인데 이또한 Dispatch를 Props로 변경해주는 내용이라고(어려워서) 그냥 속편하게 나는 생각해버렸다.