[리액트 프로그래밍 정석] 리덕스로 데이터 관리하기_2
화면 컴포넌트와 데이터 컴포넌트 구분하기
화면 컴포넌트
- 컴포넌트를 화면에 출력하는데 집중한다.
- 리덕스와 상관없이 프로퍼티를 통해 데이터를 전달받기만 한다.
- 데이터의 변경은 프로퍼티로 전달받은 콜백 함수를 호출하거나 state를 사용한다.
데이터 컴포넌트
- 스토어의 데이터를 컴포넌트에 전달하거나 변경하는데 집중한다.
- 리덕스와 깊은 연관이 있고 react-redux 공급자의 스토어를 연결하여 데이터를 관리한다.
- 스토어의 리듀서와 dispatch()함수를 화면 컴포넌트에 프로퍼티로 전달한다.
데이터 컴포넌트와 화면 컴포넌트 출력해 보기
connect()함수로 데이터 컴포넌트 생성하기
connect()함수의 첫번째 인자로는 mapStateToProps()함수, 두번째 인자로는 mapDispatchToProps()함수를 전달해야 한다. mapStateToProps()함수는 데이터 컴포넌트에서 필요한 데이터를 스토어에서 추출하여 객체를 반환하는 역할을 한다. connect()함수는 이 객체를 이용하여 이후 재사용되는 컴포넌트(PresentationComponent)의 프로퍼티에 전달해준다.
[07/PresentationComponent.jsx]
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
class PresentationComponent extends PureComponent {
render() {
const { userName, entity } = this.props;
return (
<div>
이름: {userName}
선택된 항목: {entity && `name: ${entity.name}, age: ${entity.age}`}
</div>
);
}
}
PresentationComponent.propTypes = {
userName: PropTypes.string,
entity: PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
age: PropTypes.number,
})
};
export default PresentationComponent;
[07/containers/ContainerComponent.jsx]
import { connect } from 'react-redux';
import PresentationComponent from '../PresentationComponent';
const mapStateToProps = (state, props) => {
return {
userName: state.user.name,
entity: state.collection.entities[props.id],
};
};
export default connect(mapStateToProps)(PresentationComponent);
[07/ActionComponent01.jsx]
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import Button from '../04/Button';
class ActionComponent extends PureComponent {
render() {
const { setAge } = this.props;
return (
<React.Fragment>
<Button onPress={() => setAge(2, 19)}>고유번호 2번 나이 19세로 변경</Button>
</React.Fragment>
);
}
}
ActionComponent.propTypes = {
setAge: PropTypes.func,
};
export default ActionComponent;
[07/containers/DispatchContainer01.jsx]
import { connect } from 'react-redux';
import ActionComponent from '../ActionComponent01';
import { setAge } from '../actions/collectionActions';
const mapDispatchToProps = dispatch => {
return {
setAge: (id, age) => dispatch(setAge(id, age)),
};
};
export default connect(null, mapDispatchToProps)(ActionComponent);
[07/AdvReduxApp07.jsx]
import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';
import configureStore from './configureStore';
import { setCollection } from './actions/collectionActions01';
import { setAge } from './actions/collectionActions02';
import ContainerComponent from './containers/ContainerComponent';
import PresentationComponent from './PresentationComponent';
import DispatchContainer01 from './containers/DispatchContainer01';
class AdvReduxApp extends PureComponent {
store = configureStore({ loading: false });
componentDidMount() {
this.store.dispatch(
setCollection([
{ id: 1, name: 'John', age: 20 },
{ id: 2, name: 'Park', age: 35 },
]),
);
this.store.dispatch(setAge(2, 55));
const { collection } = this.store.getState();
const { ids, entities } = collection;
const originalPayload = ids.map(id => entities[id]);
console.log(originalPayload);
}
render() {
return (
<Provider store={this.store}>
화면 컴포넌트: <PresentationComponent userName="화면 컴포넌트" />
<br />
데이터 컴포넌트: <ContainerComponent id={2} />
<br />
액션 데이터 컴포넌트: <DispatchContainer01 />
</Provider>
);
}
}
export default AdvReduxApp;
Connect함수에 대해 자세히 알아보자면…
- connect함수는 컨테이너 컴포넌트르 만드는 또다른 방법이지만 사실 사용할 일이 별로 없다. useSelector,useDispatch가 편하기 때문이다. 우리가 리액트 컴포넌트를 만들 때에는 함수형 컴포넌트로 만드는 것을 우선시해야하고, 꼭 필요할 때에만 클래스형 컴포넌트로 작성을 해야만한다.(
2019년 이전에 작성된 리덕스와 연동된 컴포넌트들은 connect 함수로 작성되었을 것ㅠ)
만약 클래스형 컴포넌트로 작성을 하게 되는 경우에는 Hooks 를 사용하지 못하기 때문에 connect 함수를 사용해야 함. (즉 앞으로 hooks 사용법에 익숙해져야한다.)