Redux 예제 - MoonGyeongHyeon/React_Study GitHub Wiki
화면에 값이 0인 숫자가 출력되고, 숫자를 누를 때마다 값이 1씩 증가되는 예제를 구현해보도록 하자. 이 예제는 react-redux
없이 redux
만을 사용하여 구현한다.
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
const INCREMENT = "INCREMENT";
/*
* Action
*/
function increase(diff) {
return {
type: INCREMENT,
addBy: diff
};
}
/*
* Reducer
*/
const initialState = {
value: 0
};
const counterReducer = (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return Object.assign({}, state, {
value: state.value + action.addBy
});
default:
return state;
}
};
/*
* Store
*/
const store = createStore(counterReducer);
class App extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
render() {
console.log('aa');
let centerStyle = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
WebkitUserSelect: 'none',
MozUserSelect: 'none',
MsUserSelect:'none',
userSelect: 'none',
cursor: 'pointer'
};
return (
<div
onClick={ this.onClick }
style={ centerStyle }
>
<h1> {this.props.store.getState().value} </h1>
</div>
)
}
onClick() {
this.props.store.dispatch(increase(1));
}
}
const render = () => {
const appElement = document.getElementById('app');
ReactDOM.render(
<App store={store}/>,
appElement
);
};
store.subscribe(render);
render();
코드를 하나하나씩 나눠서 보도록 하자.
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
Redux 라이브러리 중 store를 생성시키는 createStore 메소드를 불러온다.
const INCREMENT = "INCREMENT";
/*
* Action
*/
function increase(diff) {
return {
type: INCREMENT,
addBy: diff
};
}
action 관련 부분이다. type 정의를 위한 INCREMENT를 미리 선언해두고, action 객체를 반환하는 increment 함수를 정의한다.
/*
* Reducer
*/
const initialState = {
value: 0
};
const counterReducer = (state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return Object.assign({}, state, {
value: state.value + action.addBy
});
default:
return state;
}
};
action 객체를 처리하는 reducer 이다. 초기 상태(initialState)를 정의해두고, reducer 함수를 정의하고 있다. 매개 변수 state의 경우, 값이 없다면 initialState가 default parameter가 된다. (default parameter는 ES6에서 추가됐다.)
state의 값을 직접적으로 수정하지 않는다. state를 복사한 뒤, action에 따라 값을 처리하고 그 결과를 반환한다.
Object.assign 메소드는 첫 번째 매개변수는 꼭 비어있는 객체를 넘긴다.
/*
* Store
*/
const store = createStore(counterReducer);
처음에 import했던 createStore 메소드를 호출하여 store를 생성한다. 인자로는 reducer 함수를 넣는다.
class App extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
render() {
let centerStyle = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
WebkitUserSelect: 'none',
MozUserSelect: 'none',
MsUserSelect:'none',
userSelect: 'none',
cursor: 'pointer'
};
return (
<div
onClick={ this.onClick }
style={ centerStyle }
>
<h1> {this.props.store.getState().value} </h1>
</div>
)
}
onClick() {
this.props.store.dispatch(increase(1));
}
}
App 컴포넌트이다. 뒤에서 나오겠지만, App 컴포넌트를 생성할 때, 초기화가 끝난 store 객체가 속성으로 넘어온다.
getState 메소드를 통해 store에 저장된 state 값을 참조할 수 있는 객체가 반환된다.
숫자를 눌렀을 때의 이벤트인 onClick 메소드는 store의 dispatch 메소드를 호출한다. dispatch 메소드는 인자로 action을 받는다.(increase 함수의 리턴 값은 action 객체.)
const render = () => {
const appElement = document.getElementById('app');
ReactDOM.render(
<App store={store}/>,
appElement
);
};
store.subscribe(render);
render();
ReactDOM 객체를 이용하여 바로 렌더링을 하는게 아니라, 그러한 과정을 담는 render하는 함수를 미리 정의해놓는다. 그리고 그 함수를 store의 subscribe 메소드로 등록시킨다. 그렇게 되면, 이제 dispatch 메소드가 실행될 때 리스너 함수도 같이 실행된다. 즉, 데이터에 변동이 있을 때마다 리렌더링하도록 설정된다.
이러한 방식으로 코딩을 하면 컴포넌트 개수가 더 늘어날 경우, child에게 store를 계속해서 전달해주어야 하니 번거로워진다. 간결한 코드를 위해 react-redux 모듈을 사용하면 더 편해질 수 있다.