Redux #6 리액트-리덕스 연동 컴포넌트 예시
- 이번 포스팅은 리덕스를 사용할 때 컴포넌트를 어떤 식으로 나누어서 만드는지를 알아보고 container component에서 리덕스를 사용하고 presentational component에서 UI에만 집중하고 필요한 데이터를 props를 통해 받아와서 관심사의 분리를 하는 방법을 알아본다.
- 아래 예시는 input에 숫자를 조절하면 diff값이 바뀌어서 +, -를 눌렀을 때 그 값만큼 변하는 예제다.
- presentational component
리덕스 스토어에 직접적으로 접근하지 않고 필요한 값, 함수를 props로만 받아와서 사용하는 컴포넌트 - container component
액션을 디스패치 하거나 store에 조회하는 컴포넌트
1. presentational component
components/Counter.js
import React from 'react';
export default function Counter({ number, diff, onIncrease, onDecrease, onSetDiff }) {
const onChange = e => {
onSetDiff(parseInt(e.target.value), 10); // 기본적으로 input의 value는 string이다. 그래서 변환 필요.
};
return (
<div>
<h1>{number}</h1>
<div>
<input type='number' value={diff} onChange={onChange} />;
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
}
2. container component
- useSelector는 상태를 조회.
state는 현재 리덕스의 상태에서 어떤 것을 불러올지를 정해서 비구조화할당으로 꺼낸다. - useDispatch를 쓰면 dispatch를 사용할 수 있게 된다.
함수를 만들어서 함수가 실행되면 액션이 디스패치되게 한다.
container/CounterContainer.js
import React from 'react';
import Counter from '../components/Counter';
// 상태를 조회하기 위해 useSelector 사용
// 액션을 dispatch하기 위해 useDispatch 사용
import { useSelector, useDispatch } from 'react-redux';
import { increase, decrease, setDiff } from '../modules/counter';
export default function CounterContainer() {
const { number, diff } = useSelector(state => ({
// state에는 state.getState()한 것과 같은 값이 들어있다.
number: state.counter.number, // number에는 state -> counter 안에 있는 number를 넣어주겠다.
diff: state.counter.diff,
}));
// useSelector의 결과물이 두 가지를 선택한 객체가 된다.
// 그래서 number, diff만 남는 객체가 되고 비구조화할당으로 두 가지를 꺼내온 것이다.
// 👏 즉, 꺼내올 값들을 useSelector를 이용해서 객체의 키로 지정해주고 비구조화할당으로 꺼낸 것.
//--------
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
};
const onDecrease = () => {
dispatch(decrease());
};
const onSetDiff = diff => {
dispatch(setDiff(diff));
};
return (
<Counter
number={number}
diff={diff}
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetDiff={onSetDiff}
/>
);
}
App.js
지난번 포스팅에서 index.js에 Provider로 감싸주었고 store를 전달해줬었다.
import CounterContainer from './containers/CounterContainer';
function App() {
return (
<div className='App'>
<CounterContainer />
</div>
);
}
export default App;
정리
컨테이너와 프레젠테이셔널 컴포넌트를 분리해서 작성하면 프레젠테이셔널 컴포넌트를 재활용해줄 수 있다. 관심사를 분리할수도 있어서 굉장히 유용한 패턴이다. 리덕스의 창시자가 이런 패턴을 공개했다.(2019년에 나눠서 해도 되는데 꼭 그렇게 할 필요는 없다고 코멘트를 남겼다. 하지만 대다수 프로젝트가 이렇게 진행된다.) 벨로퍼트님은 폴더를 따로 나누지는 않지만 컨테이너와 프로젠테이셔널을 구분해서 작업하신다고 한다.
아래는 이전 포스팅 참고 코드다. 위 코드를 분석할 때 바로 참고할 수 있도록 넣어둔다.
modules/counter.js
// 액션 타입 선언
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
const initialState = {
number: 0,
diff: 1,
};
// 액션 생성 함수
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
// reducer
export default function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return {
...state,
number: state.number + state.diff,
};
case DECREASE:
return {
...state,
number: state.number - state.diff,
};
case SET_DIFF:
return {
...state,
diff: action.diff,
};
default:
return state;
}
}
modules/index.js
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const reducer = combineReducers({ counter, todos });
export default reducer;
Author And Source
이 문제에 관하여(Redux #6 리액트-리덕스 연동 컴포넌트 예시), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@joker77z/Redux-6-리액트-리덕스-연동-컴포넌트-예시저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)