자바스크립트 애플리케이션을 위한 상태 관리 라이브러리
state 내부 상태 관리
props 구성요소가 서로 통신하는 방법 - 상위 구성 요소에서 아래쪽으로 흐른다.
View -> dispatch(aciton) -> midderware -> Reducer -> Redux Store -> React Component -> View
- View에서 액션이 일어난다.
- dispatch에서 action이 발생한다.
- action에 의한 reducer 함수가 실행되기 전에 middleware가 작동한다.
- middleware에서 명령내린 일을 수행하고 난 뒤, reducer 함수를 실행한다.
- reducer의 실행결과 store에 새로운 값을 지정한다.
- store의 state에 subscribe를 하고 있던 UI에 변경된 값을 준다.
패키지 설치: yarn add react-redux
기본적인 구성 순서
1. reducer 파일 생성
2. store 생성 : const store = creatStore(counter)
3. getState() : value={store.getState()}
4. dispatch () : store.dispatch({type: "지정한 type"})
5. subscrive()를 사용하여 반영 : index.js => store.subscribe(render)
createStore()은 store을 생성하는 역할을 하고,
combineReducers()는 각각 생성된 리듀서를 합치는 역할을 한다.
// redux > config > configStore.js - 중앙 데이터 관리소(store) 설정
import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter";
import users from "../modules/users";
import todos from "../modules/todos";
const rootReducer = combineReducers({
counter,
users,
todos,
});
const store = createStore(rootReducer);
export default store;
리듀서란 state를 action의 type에 따라 변경하는 함수이다.
action 발생 시 type과 payload를 받아와 처리하여 store로 넘겨준다.
(1) 액션 타입 정의
// redux > modulwa > counter.js
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "counter/MINUS_ONE";
const PLUS_N = "counter/PLUS_N";
const MINUS_N = "counter/MINUS_N";
(2) 액션 생성자 (액션 생성 함수) : action value를 반환하는 함수 정의
export const plusOne = () => {
return { type: PLUS_ONE };
};
export const minusOne = () => {
return { type: MINUS_ONE };
};
export const plusN = (payload) => {
return {
type: PLUS_N,
payload,
};
};
export const minusN = (payload) => {
return {
type: MINUS_N,
payload,
};
};
Payload
action 객체는 action type을 payload 만큼 처리한다.
-> 리듀서로 보내는 액션 객체에 어떤 정보를 같이 담아보내고자 한다면 payload를 이용한다.
왜 액션 타입을 정하고 생성자 함수를 사용할까?
-> 리덕스를 사용할 때 대부분의 사람들이 사용하는 Ducks 패턴을 적용하는 것이 좋기 때문이다.
Ducks 패턴
리덕스의 구성요소를 패턴화한 것
1) Reducer 함수를 export default 한다.
2) Action creator 함수들을 export 한다.
3) Action type은 app/reduce/ACTION_TYPE 형태로 작성한다.
(3) 리듀서 함수 생성
// 초기 상태값(state)
const initialState = {
number: 0,
};
// input : state와 action(type과 value를 가짐)
const counter = (state = initialState, action) => {
switch (action.type) {
case PLUS_ONE:
return { number: state.number + 1 };
case MINUS_ONE:
return { number: state.number - 1 };
case PLUS_N:
return {
number: state.number + action.payload,
};
case MINUS_N:
return { number: state.number - action.payload };
default:
return state;
}
};
export default counter;
리듀서는 state와 action을 받아서 사용한다.
(action은 type과 payload를 가지고 있다.)
dispatch로 action을 받아오고 action.type에 따라 state와 action.payload를 사용하여 값을 리턴할 수 있다.
일반적으로 switch문을 사용하며 반드시 default를 정해주어야 한다.
Redux Store 저장소에 액세스해야 하는 모든 중첩 구성 요소에서 Redux Store 저장소를 사용할 수 있도록 한다.
최상위에 존재하도록 Provider로 하위 컴포넌트를 감싸준다.
//index.js
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);
store에 저장된 데이터 상태를 가쟈온다.
- useSelector로 값을 가져올 경우 state는 store를 의미한다.
- 구독하고 있는 대상의 값이 바뀌면 리렌더링 된다.
-> 불필요한 리렌더링을 방지하기 위해 구독 대상을 명확히 하는 것이 좋다const counter = useSelector((state) => { return state.counter; // 구독 대상 (바뀌면 리렌더링) });
데이터 상태 변경을 요청한다.
// useDispatch()
const dispatch = useDispatch();
onClick={() => {
dispatch(plusN((number)));
}}