리덕스 모듈이란,
액션과 리듀서를 따로 작성하는 경우가 있지만, 이번 실습에서는 한 파일에 몰아서 작성한다.
이때 사용하는 패턴을 Ducks패턴이라고 하는데, ducks패턴은 리듀서와 액션관련 코드들을 하나의 파일에 몰아서 작성하는 방식이다.
counter.js
와 todos.js
를 작성한다.
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
//액션 생성함수
export const setDiff = diff => ({ type: SET_DIFF, diff});
export const increase = () => ({ type:INCREASE});
export const decrease = () => ({type:DECREASE});
// 초기상태
const initialState = {
number: 0,
diff: 1
};
// 리듀서 선언
export default function 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;
}
}
const ADD_TODO = 'todos/ADD_TODO';
const TOGGLE_TODO = 'todos/TOGGLE_TODO';
//액션 생성함수
let nextId = 1;
export const addTodo = text => ({
type: ADD_TODO,
todo: {
id: nextId++,
text
}
});
export const toggleTodo = id => ({
type: TOGGLE_TODO,
id
});
//초기상태
const initialState = [
/* {
id: 1,
text:"어서오고",
done: false
} */
];
export default function todos(state=initialState, action){
switch(action.type) {
case ADD_TODO:
return state.concat(action.todo);
case TOGGLE_TODO:
return state.map(
todo =>
todo.id === action.id // id 일치하면
?{...todo, done: !todo.done}
: todo
);
default:
return state;
}
}
이 두개의 리듀서를 합친 루트 리듀서를 제작한다. combineReducers
를 이용한다.
index.js
를 생성한다.
import {combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers ({
counter,
todos
});
export default rootReducer;
todos
의 경우 이전에 만들었던 todolist 프로젝트에 리덕스를 적용할 수 있는데 그렇게 하려면 먼저 react-redux
를 설치해야한다.
그 다음 루트 경로의 index.js
에 Provider, createStore, rootReducer를 불러와서
rootReducer
가 들어있는 스토어 store
를 생성한뒤 콘솔 로그로 출력시켜보자.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import rootReducer from './modules';
const store = createStore(rootReducer);
console.log(store.getState());
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
콘솔 화면에
이렇게 2개의 리듀서가 나타난다.
리듀서를 리액트 프로젝트에 적용시키는 방법은 index
에서 App
을 Provider
에 감싼다. 이때, Provider
의 값에 store를 추가시킨다.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
리덕스 스토어에 직접적으로 접근하지 않고 필요한 값 또는 함수를 props 로만 받아와서 사용하는 컴포넌트이다.
import React from "react";
function Counter({number, diff, onIncrease, onDecrease, onSetDiff}) {
const onChange = e => {
onSetDiff(parseInt(e.target.value, 10));
}
return (
<div>
<h1>{number}</h1>
<div>
<input type="number" value={diff} min="1" onChange={onChange}></input>
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
}
export default Counter;
전반적으로 UI선언에 집중하고 필요한 값을 props로 받아와서 사용한다.
리덕스 스토어 상태조회, 액션을 디스패치 할수 있는 컴포넌트를 뜻한다. HTML태그를 사용하지않고, 다른 프레진테이셔널 컴포넌트들을 불러와서 사용한다.
import React from "react";
import {useSelector, useDispatch} from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff} from '../modules/counter';
function CounterContainer() {
const {number,diff} = useSelector(state => ({
number: state.counter.number,
diff: state.counter.diff
}));
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}
/>
);
}
export default CounterContainer;
App에 CounterContainer
컴퍼넌트를 추가하고 실행해보면
이렇게 입력한 숫자만큼 더해지는 모습을 볼수 있다.