리덕스란, props문법 등을 사용하지 않고 모든 컴포넌트에서 글로벌하게 state에 접근하고 수정하기 위한, State의 관리 방법이다. 리듀서(reducer)는 리덕스에서 관리하는 state의 상태 변경을 관장하는 함수이다. 사용자는 특정 신호를 dispatch라는 문법을 통해 리덕스에게 전달하여 state를 수정해달라 요청하고, 리덕스에서 해당 state의 리듀서를 수행한 뒤 수정 결과를 반영한다.
yarn add redux react-redux // 두 개 모듈 설치
Redux의 3가지 규칙
하나의 애플리케이션안에는 하나의 Store가 있다.
store는 단 하나만을 생성해서 상태를 관리할 수 있다. (여러개의 store를 사용할 수는 있지만 리덕스에서 권장하는 방법은 아니다) 그 대신 reducer를 여러개 만들어서 다양한 상태를 구분해 관리할 수 있다.
상태는 읽기전용 이다.
리액트에서 상태에 불변성을 유지시켜주는 것과 비슷하다. 기존의 상태는 건드리지 않고 새로운 상태를 생성해 상태를 변경시켜준다. 마치 setState처럼 말이다. 리덕스에서 불변성을 유지해야 하는 이유는 내부적으로 상태가 변경되었는지 검사를 확인하기 위해 shallow equality검사를 하기 때문이다.
변화를 일으키는 함수, Reducer는 순수한 함수여야 한다.
reducer함수는 상태와 액션 객체를 인자로 받게 되는데 이때 이전의 상태를 변경하는 것이 아닌 새로운 상태 객체를 만들어서 반환해야 한다. 그리고 같은 인자로 호출된 reducer는 언제나 같은 값을 반환하는 순수 함수로 만들어 져야 한다.
동일한 인풋이라면 동일한 아웃풋이 있어야 한다. 그러나 일부 로직들 중 실행 할 때마다 다른 결과값이 나타날 수 있다. new Date()나 Math.random(), 네트워크 요청 등이 있는데 이러한 작업들은 결코 순수하지 않은 작업이므로, Reducer 함수의 바깥에서 처리해줘야 한다. 이런 것을 하기 위해 Redux Middleware를 사용하곤 한다.
사용방법
1. 리듀서 등록(reducers/reducer1.js, reducer2.js)
import React from "react";
// 초기값을 지정하기 위한 데이터
let defaultStates = [
{ id: 0, name : "김영철", age : 18 },
{ id: 1, name : "이바보", age : 25 },
{ id: 2, name : "박순이", age : 17 },
{ id: 3, name : "구팔찌", age : 35 }
]
// 순수 함수형태로 return되는 값은 항상 state여야 한다.(불변 법칙)
export default function reducer1(state = defaultStates, action) {
if(action.type === 'addAge') {
let copy = [...state]
copy[action.id].age += 1
return copy
} else if(action.type === 'minusAge') {
let copy = [...state]
copy[action.id].age -= 1
return copy
} else {
return state
}
}
export function reducer2(state = true, action) {
return state;
}
2. index.js 에 설치한 모듈 설정
// combineReducers로 여러 개의 리듀서를 합쳐 store에 저장한다.
import {combineReducers, createStore} from "redux";
import {Provider} from "react-redux";
import reducer1 from "./reducers/reducer1";
import reducer2 from "./reducers/reducer2";
let store = createStore(combineReducers({reducer1, reducer2}));
// 아래 App 사이에 Provider 블록으로 감싸고 store에 위에 만든 리덕스 store를 링크한다.
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
3. 필요한 컴포넌트에서 사용(old)
- state를 props화 해주는 함수 생성
- export 구문에 위 함수와 컴포넌트를 연결
- 컴포넌트에 파라미터로 props를 추가하고 props에서 연결한 리덕스의 state를 꺼내서 사용
- 수정 요청은 연결 후에 props에 보면 dispatch가 있는데 그것을 이용하여 기본적으로는 json 객체에 type, payload에 데이터를 실어 보낸다.(즉, 리듀서는 type을 보고 state의 수정타입을 결정하고 payload에 있는 데이터를 수정사항에 반영한다)
function Cart(props) {
let props.받아온값
return (
<div>
{
props.받아온값.map((value, index) => {
console.log(value);
}
}
<button onClick={() => {
props.dispatch({ type : "action.type에 넣을 것", payload : { 수정을 위한 여러 데이터 } });
}}></button>
</div>
)
}
function getState(state) {
return {
받아온값 : state
}
}
export default connect(getState)(Cart)
3-1. 위 방식 너무 불편해서 새롭게 사용하는 방법
- useSelector로 링크 없이 리덕스의 state를 꺼내온다.
- useDispatch로 데이터 수정을 요청한다.
import {useSelector, useDispatch} from 'react-redux';
function Cart(props) {
let 받아온값 = useSelector((state) => state); // 객체 자체를 리턴해서 받음
let dispatch = useDispatch();
return (
<div>
{
받아온값.map((value, index) => {
console.log(value);
}
}
<button onClick={() => {
dispatch({ type : "action.type에 넣을 것", payload : { 수정을 위한 여러 데이터 } });
}}></button>
</div>
)
}
export default Cart;
댓글