반응형
src/modules/counter.js
/* 액션 타입 만들기 */
// Ducks 패턴을 따를땐 액션의 이름에 접두사를 넣어주세요.
// 이렇게 하면 다른 모듈과 액션 이름이 중복되는 것을 방지 할 수 있습니다.
const SET_DIFF = "counter/SET_DIFF";
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
/* 액션 생성함수 만들기 */
// 액션 생성함수를 만들고 export 키워드를 사용해서 내보내주세요.
export const setDiff = (diff) => ({ type: SET_DIFF });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
/* 초기 상태 선언 */
const initialState = {
number: 0,
diff: 1,
};
/* 리듀서 선언 */
// 리듀서는 export default 로 내보내주세요.
const counter = (state = initialState, action) => {
switch (action.type) {
case SET_DIFF:
return {
...state,
diff: action.diff,
};
case INCREASE:
return {
...state,
number: state.number + state.diff,
};
case DECREASE:
return {
...state,
number: state.number - state.diff,
};
default:
return state;
}
};
export default counter;
src/modules/todos.js
/* 액션 타입 선언 */
const ADD_TODO = "todos/ADD_TODO";
const TOGGLE_TODO = "todos/TOGGLE_TODO";
/* 액션 생성함수 선언 */
let nextId = 1; // todo 데이터에서 사용 할 고유 id
export const addTodo = (text) => ({
type: ADD_TODO,
todo: {
id: nextId++, // 새 항목을 추가하고 nextId 값에 1을 더해줍니다.
text,
},
});
export const toggleTodo = (id) => ({
type: TOGGLE_TODO,
id,
});
/* 초기 상태 선언 */
// 리듀서의 초기 상태는 꼭 객체타입일 필요 없습니다.
// 배열이여도 되고, 원시 타입 (숫자, 문자열, 불리언 이여도 상관 없습니다.
const initialState = [
/* 우리는 다음과 같이 구성된 객체를 이 배열 안에 넣을 것입니다.
{
id: 1,
text: '예시',
done: false
}
*/
];
const todos = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return [...state, action.todo];
case TOGGLE_TODO:
return state.map((todo) =>
todo.id === action.id ? { ...todo, done: !todo.done } : { ...todo }
);
default:
return state;
}
};
export default todos;
src/modules/index.js
(없어도 되나 combineReducers를 테스트하기 위해 todos.js 생성)
import { combineReducers } from "redux";
import counter from "./counter";
import todos from "./todos";
const rootReducer = combineReducers({
counter,
todos,
});
export default rootReducer;
프리젠테이셔널 컴포넌트 만들기
프리젠테이셔널 컴포넌트란, 리덕스 스토어에 직접적으로 접근하지 않고 필요한 값 또는 함수를 props 로만 받아와서 사용하는 컴포넌트입니다.
(MVP에서 View에 해당한다.)
src/components/Counter.js
import React from "react";
const Counter = ({ number, diff, onIncrease, onDecrease, onSetDiff }) => {
const onChange = (e) => {
// e.target.value 의 타입은 문자열이기 때문에 숫자로 변환해주어야 합니다.
onSetDiff(parseInt(e.target.value, 10));
};
return (
<div>
<h1>{number}</h1>
<div>
<input type="number" value={diff} min="1" onChange={onChange} />
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
};
export default Counter;
컨테이너 컴포넌트 만들기
컨테이너 컴포넌트란, 리덕스 스토어의 상태를 조회하거나, 액션을 디스패치 할 수 있는 컴포넌트를 의미합니다. 그리고, HTML 태그들을 사용하지 않고 다른 프리젠테이셔널 컴포넌트들을 불러와서 사용합니다.
(MVP에서 Presenter에 해당한다.)
src/containers/CounterContainer.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import Counter from "../components/Counter";
import { increase, decrease, setDiff } from "../modules/counter";
const CounterContainer = () => {
// useSelector는 리덕스 스토어의 상태를 조회하는 Hook입니다.
// state의 값은 store.getState() 함수를 호출했을 때 나타나는 결과물과 동일합니다.
const { number, diff } = useSelector((state) => ({
number: state.counter.number,
diff: state.counter.diff,
}));
// useDispatch 는 리덕스 스토어의 dispatch 를 함수에서 사용 할 수 있게 해주는 Hook 입니다.
const dispatch = useDispatch();
// 각 액션들을 디스패치하는 함수들을 만드세요
const onIncrease = () => dispatch(increase());
const onDecrease = () => dispatch(decrease());
const onSetDiff = () => dispatch(setDiff(diff));
return (
<Counter
// 상태와
number={number}
diff={diff}
// 액션을 디스패치 하는 함수들을 props로 넣어준다.
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetDiff={onSetDiff}
/>
);
};
export default CounterContainer;
반응형
'Basic > React' 카테고리의 다른 글
[React] Custom hook 모음 (0) | 2023.01.02 |
---|---|
dashboard design example (0) | 2021.03.24 |
Prittier 저장 시 자동으로 코드 정리하기 (0) | 2021.03.23 |
React를 이용한 다중 input 상태 관리 (0) | 2021.03.19 |
React 스타일 조건부 렌더링 (0) | 2021.03.18 |