액션을 만드는 생성자. 액션 객체를 만드는 함수?
액션객체의 value를 변경할 일이 생긴다면 어떨까 PLUS_ONE ,MINUS_ONE 이라는 value 대신 이 액션객체가 counter 모듈안에 있다는 것을 강조하기 위해서 counter/PLUS_ONE, counter/MINUS_ONE 이라는 value로 바꾸길 각각 바꾸길 원한다면 아래 코드에서 4번이나 변경해줘야 한다. 💢
// src/App.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.counter.number);
return (
<div>
{number}
<button
onClick={() => {
dispatch({ type: "PLUS_ONE" }); // counter/PLUS_ONE로 변경
}}
>
+ 1
</button>
<button
onClick={() => {
// 액션객체 디스패치
dispatch({ type: "MINUS_ONE" }); // counter/MINUS_ONE로 변경
}}
>
- 1
</button>
</div>
);
};
export default App;
// src/modules/counter.js
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case "PLUS_ONE": // counter/PLUS_ONE로 변경
return {
number: state.number + 1,
};
// action.type이 MINUS_ONE 일 때 새로운 state 반환
case "MINUS_ONE": // counter/MINUS_ONE로 변경
return {
number: state.number - 1,
};
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
액션객체를 한 곳에서 관리할 수 있도록 함수와 액션 value를 상수로 만들어보자
PLUS_ONE 액션 객체를 만드는 함수 생성
=> Action Creator 액션을 만드는 생성자
// src/redux/modules/counter.js
const PLUS_ONE = "PLUS_ONE"; // value는 상수로 생성
// 액션객체를 반환하는 함수 생성
// export 가 붙는 이유는 plusOne()는 밖으로 나가서 사용될 예정이기 때문입니다.
export const plusOne = () => {
return {
type: PLUS_ONE, // type에는 위에서 만든 상수로 사용 (vscode에서 자동완성 지원)
};
};
모듈에 initialState와 리듀서밖에 없었지만
액션의 value와Action Creator가 추가
// src/modules/counter.js
// 추가된 코드 👇 - 액션 value를 상수들로 만들어 줍니다. 보통 이렇게 한곳에 모여있습니다.
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";
// 추가된 코드 👇 - Action Creator를 만들어 줍니다.
export const plusOne = () => {
return {
type: PLUS_ONE,
};
};
export const minusOne = () => {
return {
type: MINUS_ONE,
};
};
// 초기 상태값
const initialState = {
number: 0,
};
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case PLUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다.
return {
number: state.number + 1,
};
case MINUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다.
return {
number: state.number - 1,
};
default:
return state;
}
};
export default counter;
생성한 Action Creator를 컴포넌트에서 어떻게 사용하는지
export 된 Action Creator import 하기 dispatch()에 있던 액션객체를 지우고, Action creator 넣기// src/App.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
// 사용할 Action creator를 import 합니다.
import { minusOne, plusOne } from "./redux/modules/counter";
const App = () => {
const dispatch = useDispatch();
const number = useSelector((state) => state.counter.number);
return (
<div>
{number}
<button
onClick={() => {
dispatch(plusOne()); // 액션객체를 Action creator로 변경합니다.
}}
>
+ 1
</button>
{/* 빼기 버튼 추가 */}
<button
onClick={() => {
dispatch(minusOne()); // 액션객체를 Action creator로 변경합니다.
}}
>
- 1
</button>
</div>
);
};
export default App;
Q. dispatch()안에는 반드시 객체만 들어가야 한다고 알고 있는데, 어떻게 함수가 들어갈 수 있을까요?
A. {type: “PLUS_ONE”} === plusOne() 는 같은 값
함수를 실행한 것은 함수의 return 값과 같습니다.
다시 말해, const one = () => {return 1; } 로 함수를 만들었을 때 one() === 1 이다.
액션객체의 type value를 상수로 만들어놓았기 때문에, 개발툴에서 자동완성등의 보조 기능을 지원받을 수 있다.
Action Creator가 만약 100군데에서 쓰이고 있는 상태에서 혹여나 그것을 바꾸어야 하는 상황이 오더라도 단 한번의 수정으로 100군데에 모든 수정사항을 반영할 수 있다.
모듈 파일에서 Action Creator가 일목요연하게 정리가 되어있으면, 다른 개발자가 보았을 때 해당 모듈이 가지고 있는 모든 Action들을 한눈에 알 수 있게 된다.
https://redux.js.org/api/bindactioncreators/#todoactioncreatorsjs
reducer에 얼만큼의 값을 어떻게 해! 라고 할 때, 이 얼만큼의 값을 전달해 줄 수 있는 것이payload🥇
이전 챕터에서+1 기능과 -1 기능을 만들었다.
BUT 1로 정해진 기능을 만드는 것이 아니라 증가 시킬 숫자를 사용자가 직접 정할 수 있게하는 기능을 만들려면?
사용자가 5을 더하고 싶으면 input에 5를 입력해서 버튼을 누르면 5가 더해지고, 10을 더하고 싶으면 10을 입력하고 버튼을 눌렀을 때 10이 더해지는 프로그램을 만들려면?
우리가 이전까지 만든 기능은 리듀서에게 "플러스해" 라고만 말을 했다. 1이라는 것은 우리가 임의적으로 정해서 리듀서에게 액션 객체를 보냈다.
하지만 우리는 이제 "N을 플러스해" 라고 N을 같이 리듀서에서 보내야 한다.
지금까지는 ~을 이라는 목적어가 없었다면, 이제는 그 목적어가 생긴것이고 목적어도 액션객체에 담아 같이 보내줘야 한다.
이렇게 액션객체에 같이 담아 보내주는 게 payload
type뿐만 아니라
payload라는 key와 value를 같이 담는다.// payload가 추가된 액션객체 {type: "ADD_NUMBER", payload: 10}
값을 입력받을 input 구현 -> Action Creator 작성 -> 리듀서 작성 -> 구현 기능 테스트
// src/App.js
import React from "react";
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0);
const onChangeHandler = (event) => {
const { value } = event.target;
setNumber(+value);
};
// 콘솔로 onChangeHandler가 잘 연결되었는지 확인
// input에 값을 넣을 때마다 콘솔에 그 값이 찍히면 연결 성공!
console.log(number);
return (
<div>
<input type="number" onChange={onChangeHandler} />
<button>더하기</button>
<button>빼기</button>
</div>
);
};
export default App;
// src/redux/modules/counter.js
// Action Value
// ES6에서 객체 key, value가 같으면 이부분 생략 가능
const ADD_NUMBER = "ADD_NUMBER";
// Action Creator
export const addNumber = (payload) => {
return {
type: ADD_NUMBER,
payload: payload,
};
};
// Initial State
// Reducer
// export default reducer
// src/redux/modules/counter.js
// .. 중략
// Initial State
const initialState = {
number: 0,
};
// Reducer 기본형태
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
// export default reducer
export default counter;
initial State와 리듀서의 기본 형태를 만들어준다.
export default 를 통해서 생성한 리듀서를 내보내준다.
ADD_NUMBER의 로직 구현
// 리듀서
const counter = (state = initialState, action) => {
switch (action.type) {
case ADD_NUMBER:
return {
// state.number (기존의 nubmer)에 action.paylaod(유저가 더하길 원하는 값)을 더한다.
number: state.number + action.payload,
};
default:
return state;
}
};
App.js
import React from "react";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
// 4. Action Creator를 import
import { addNumber } from "./redux/modules/counter";
const App = () => {
// 1. dispatch를 사용하기 위해 선언
const dispatch = useDispatch();
const [number, setNumber] = useState(0);
const globalNumber = useSelector((state) => state.counter.number);
const onChangeHandler = (event) => {
const { value } = event.target;
setNumber(+value);
};
// 2. 더하기 버튼을 눌렀을 때 실행할 이벤트핸들러를 생성
const onClickAddNumberHandler = () => {
// 5. Action creator를 dispatch 해주고, 그때 Action creator의 인자에 number를 넣어줍니다.
dispatch(addNumber(number));
};
return (
<div>
<div>{globalNumber}</div>
<input type="number" onChange={onChangeHandler} />
{/* 3. 더하기 버튼 이벤트핸들러를 연결 */}
<button onClick={onClickAddNumberHandler}>더하기</button>
<button>빼기</button>
</div>
);
};
export default App;
Ducks 패턴은 Redux 앱을 구성할 때 사용하는 방법론 중 하나로, 일반적으로 분산되어 있던 액션 타입, 액션 생성자, 리듀서를 하나의 파일로 구성하는 방식. 코드의 관리를 보다 간결하고 모듈화하여 관리할 수 있도록 도와준다.
🐥 Erik Rasmussen 이 제안한 Ducks 패턴
Reducer 함수를 export default 한다.
Action creator 함수들을 export 한다.
Action type은 app/reducer/ACTION_TYPE 형태로 작성한다.
🐥 모듈 파일 1개에 Action Type, Action Creator, Reducer 가 모두 존재하는 작성방식