이 글은 벨로퍼트님의 Redux (3) 리덕스를 리액트와 함께 사용하기 를 보고 작성한 글입니다.
이 글을 보시기 전에 리액트 기초을 보고오세요.
+ 버튼을 클릭하면 Counter가 증가
- 버튼을 클릭하면 Counter가 감소하는 웹을 만들어 보겠습니다.
먼저 프로젝트를 생성합니다.
> create-react-app react-redux-counter
react-redux-counter 디렉토리에 들어가 필요한 모듈을 설치합니다.
npm install --save redux react-redux
import React from 'react' ;
const Counter = () => {
return (
<div>
<h1>myCounter</h1> // 카운터를 표시해줄 부분
<button>+</button> // 증감 버튼
<button>-</button> // 감소 버튼
</div>
) ;
} ;
export default Counter ;
import React, { Component } from 'react';
import './App.css';
import Counter from './components/Counter';
class App extends Component {
render() {
return (
<div className="App">
<Counter />
</div>
);
}
}
export default App;
액션: 상태 변화를 일으킬 때 참조하는 객체입니다.
우리는 Counter 값을 변화시키는데 + 버튼, - 버튼을 이용하여 증.감소를 합니다.
그럼 만들어야할 액션은 두가지 + 버튼, -버튼입니다.
* Store에 대해 뭔가 하고싶은 경우에 Action을 발행한다.
* Store의 문지기(Reducer)가 Action을 감지하면, 새로운 State가 생긴다.
// 액션 타입 정의
const INCREMENT = 'counter/INCREMENT' ;
const DECREMENT = 'counter/DECREMENT' ;
// 액션 생성 함수 정의
export const increment = () => ({ type: INCREMENT }) ;
export const decreuemt = () => ({ type: DECREMENT }) ;
// 초기 상태 정의
const initialState = {
number: 0
} ;
리듀서는 상태에 변화를 일으키는 함수이다. 리듀서는 파라미터를 두 개를 받습니다.
첫번째 파라미터는 현재상태이고, 두번째 파라미터는 액션 객체입니다.
// 리듀서 작성
export default function Counter(state=initialState, action) {
switch(action.type) {
case INCREMENT:
return {
...state,
number: state.number + 1 ,
} ;
case DECREMENT:
return {
...state,
number: state.number - 1,
} ;
default:
return state ;
}
}
redux의 내장함수인 combineReducers를 사용하여 리듀서를 하나로 합치는 작업을한다.
아직은 하나의 리듀서뿐이지만..!
import { combineReducers } from 'redux' ;
import counter from './counter' ;
export default combineReducers({
counter,
//다른 리듀서를 만들게 되면 여기에 import
}) ;
스토어는 하나의 애플리케이션 에는 하나의 스토어가 있다.
우리도 딱 하나의 스토어만 만들어주면 됩니다.
import React from 'react';
import ReactDOM from 'react-dom';
// createStore 와 루트 리듀서 불러오기
import { createStore } from 'redux';
import rootReducer from './store/modules';
import { Provider } from 'react-redux' ;
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
// **** 리덕스 개발자 도구 적용
const devTools =
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__();
const store = createStore(rootReducer, devTools);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
여기까지 정리하자면 우리는 화면(Counter.js) 에서 +,- 버튼을 클릭하게 되면 액션을 통해 리듀서를(counter.js) 거치고 스토어 상태를 변화한 후 다시 view에 변화를 주어야한다.
우리가 한 일은
1. 화면생성 (Counter.js)
2. 액션생성 (counter.js)
3. 리듀서 생성(counter.js)
4. 스토어 생성(index.js)
이렇게하면 다 된 것 같지만 여기서 몇까지 빼먹은 것이 있다.!
1. 액션을 스토어에 전달하는 역할인 dispatch 생성 (mapDispatchToProps)
2. store에 state를 view에 전달해주기 (mapStateToProps)
3. store와 reducer를 연결시킬 수 있도록 만들어진 Component 생성 (CounterContainter.js)
4. 위의 두가지가 적용된 props를 받을 수 있는 component를 정의 (App.js)
위에서 말했다싶이, 컴포넌트에 리덕스 스토어 안에 있는 값이나 액션 함수들을 연결해주어야합니다.
리덕스와 연동된 컴포넌트를 우리는 컨테이너 컴포넌트라고 부릅니다.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import { increment, decrement } from '../store/modules/counter';
class CounterContainer extends Component { //3
handleIncrement = () => {
this.props.increment() ;
} ;
handleDecrement = () => {
this.props.decrement () ;
} ;
render() {
const { number } = this.props ;
return (
<Counter
value={number}
onIncrement={this.handleIncrement}
onDecrement={this.handleDecrement}
/>
) ;
}
}
const mapStateToProps = ({ counter }) => ({ //2
number: counter.number,
}) ;
const mapDispatchToProps = {increment, decrement} ; //1
export default connect ( // 스토어와 연결
mapStateToProps,
mapDispatchToProps
)(CounterContainer) ;
이제 우리는 스토어에서 변경된 counter(value 값),
증가, 감소 버튼을 Props로 디스패치한 것을 받아와야합니다.
import React from 'react' ;
const Counter = ({ value, onIncrement, onDecrement }) => {
return (
<div>
<h1>{value}</h1>
<button onClick={onIncrement}>+</button>
<button onClick={onDecrement}>-</button>
</div>
) ;
} ;
export default Counter ;
import React, { Component } from 'react';
import './App.css';
import CounterContainer from './container/CounterContainer.js' ;
class App extends Component {
render() {
return (
<div className="App">
<CounterContainer />
</div>
);
}
}
export default App;
버튼이 잘 동작하죠 ?
저도 리액트 초보라 잘못된 부분이 있으면 답글에 꼭 알려주세요.
고쳐나가겠습니다.
감사합니당 ㅎㅎ 깔끔하게 잘 정리해 주셔서 공부에 도움이 되었습니다!