connect
connect 함수는 리액트 앱의 하위 컴포넌트에서 redux store를 접근하는 것을 가능하게 해주는 역할을 한다. 이 함수를 이용해서 컴포넌트 단에서 redux store에 접근하고 액션을 호출 할 수 있게 된다. 이번 포스트에서는 간단한 예제로 connect 함수를 통해 redux store를 사용하는 방법을 다뤄보려고 한다.
0. 준비작업
connect 함수 소개를 위해 예제와 텍스트와 숫자를 담당하는 redux를 만들었다.
BlogStore.js
import { createStore, combineReducers } from 'redux';
const textReducerState = {
text: '',
name: 'textReducer'
};
const textReducer = (state = textReducerState, action) => {
switch (action.type) {
case 'SET_TEXT':
return {
...state,
text: action.text
};
default:
return state;
}
}
const numberReducerState = {
numberState: 30,
name: 'numberReducer'
};
const numberReducer = (state = numberReducerState, action) => {
switch (action.type) {
case 'SET_NUMBER':
return {
...state,
number: action.number
};
default:
return state;
}
};
export const configureStore = () => {
const store = createStore(
combineReducers({
text: textReducer,
number: numberReducer
})
);
return store;
};
BlogActions.js
export const setText = (
text = ''
) => ({
type: 'SET_TEXT',
text
});
export const setNumber = (
number = 0
) => ({
type: 'SET_NUMBER',
number
});
1. Provider
configureStore() 함수를 통해 store를 생성하고 Provider 태그에 store를 속성값으로 넣는다. 이러면 하위에 추가되는 component에서 redux store를 바라볼 수 있는 창구가 만들어진다.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { configureStore } from './BlogStore';
import BlogMain from './BlogMain';
const store = configureStore();
const jsx = (
<Provider store={store}>
<BlogMain />
</Provider>
);
ReactDOM.render(jsx, document.getElementById('app'));
2. connect
하위 컴포넌트 단에서는 Provider에서 제공하는 store 함수를 connect 함수를 통해서 받아온다. 함수 형식이든 클래스 형식이든 받는 방식은 동일하다.
2.1 클래스형식
import React from 'react';
import { connect } from 'react-redux';
import BlogDetail from './BlogDetail';
class BlogMain extends React.Component {
render() {
console.log(this.props.text)
console.log(this.props.number)
return (
<div>
<p>BlogMain component</p>
<BlogDetail />
</div>
)
};
};
const mapStateToProps = (state) => {
return {
text: state.text,
number: state.number
}
};
export default connect(mapStateToProps)(BlogMain);
클래스 형식 컴포넌트를 export 할 때 connect 함수를 사용하고 첫번째 인자에 mapStateToProps 함수를 넣었는데 redux store에 있는 값을 컴포넌트에 어떻게 넘겨줄지 세팅하는 작업이다. 넘겨 받은 값은 component의 props에 들어가서 호출 할 수 있다. 아래 사진은 render() 함수 안에서 console로 찍은 로그다. textReducer와 numberReducer가 출력되는 것을 볼 수 있다.
2.2 함수 형식
import React from 'react';
import { connect } from 'react-redux';
const BlogDetail = (props) => (
<div>
<p>BlogTextDetail</p>
<p>{props.text.name}</p>
</div>
);
const mapStateToProps = (state) => {
return {
text: state.text
};
};
export default connect(mapStateToProps)(BlogDetail)
함수 형식도 크게 다르지 않다. 컴포넌트 내에서 호출 할 때 this를 부르지 않아도 된다는 점만 다르다. 위 코드로 호출하면 아래 그림처럼 화면 뷰가 그려진다.
3. Action
connect로 컴포넌트에 전달 할 때 store만 전달 하는것이 아니라 action을 넣을 수 있는 dispatch 함수까지 전달된다. react 디바이스 툴을 사용해보면 component의 props 안에 dispatch가 들어있는 것을 확인 할 수 있다.
실제로도 잘 사용할 수 있을 지 테스트 해보자. 방금 전에 사용한 BlogDetail 컴포넌트를 살짝 수정해서 현재 store에 저장된 text를 출력하고 버튼을 추가하고 클릭하면 text를 BlogDetail로 바뀌도록 했다.
import React from 'react';
import { connect } from 'react-redux';
import { setText } from './BlogActions';
const BlogDetail = (props) => (
<div>
<p>BlogTextDetail</p>
<p>current store text value: {props.text.text}</p>
<button onClick={() => {
props.dispatch(setText('BlogDetail'))
}}>Change text to BlogDetail</button>
</div>
);
const mapStateToProps = (state) => {
return { text: state.text};
};
export default connect(mapStateToProps)(BlogDetail)
함수를 실행하고 버튼을 클릭하니 화면이 아래와 같이 store의 text 값이 BlogDetail로 변경됐다.
4. 총평
코딩을 처음 하는 분이면 이걸 왜 이렇게까지 해야할지 이해가 안될 수 있을 것 같은데 이전에 mvvm, mvc 패턴을 경험해본 개발자들에게는 redux가 크게 어려울 것 같지 않다. 강의 들을 때는 헷갈렸는데 실제로 코드로 짜보니까 어떤 식으로 구조를 잡아야할 지 느낌이 온다. 물론 자바스크립트 언어 특성상 state 세부 이름을 관리할 때 꽤 귀찮음을 겪을 것 같긴 하다.