1. Redux 는 Reducer 를 이용해 상태를 관리한다.
*Reducer : 프로그래밍 언어로, "입력을 받아 결과를 내는 장치" 정도 로 해석할 수 잇겠다.
useReducer와 같은 맥락이지만, 같은 것은 아니다.
변환을 위한 틀(Action) 을 저장해 두는 Reducer 함수 를 생성하고, 그 함수로 State 를 관리한다.
컴포넌트에서, 그 State 를 임의로 변형하는 것은, 매우 큰 오류를 불러 올 수 있으므로,
오로지 Reducer 함수를 통해서만, State 를 변형한다.
2. Redux 는 Store 를 만들어, 그 안에 State 를 전부 넣고 관리한다.
앞서 만든 Reducer 함수를 넣은 store 를 만들어, 그 안에 저장, 배포 한다.
3. Redux 는 "리액트 만의 전유물이 아니다"
그렇다는 것은, React 컴포넌트에서 Redux 를 "연결" 하는 장치가 필요하다는 말이 된다.
그것이 바로 "react-redux" 패키지이다.
4. 리액트 컴포넌트는, Redux 저장소와, Reducer 함수를 사용한 State 변환을 하기 위해,
react-redux 의 useDispatch, useSelector 를 이용한다.
useDispatch 는 원하는 type 의 Action 을 Reducer 에게 Dispatch(전달) 하여,
Redux의 Reducer함수로부터 State 변환을 시키게 만든다.
useSelector 는 store 에 있는 State 를 선택 하는 역할을 한다.
5. Store 의 모든 리듀서들을 모든 앱에 걸쳐 공급하려면, Provider 를 사용하여, App.js 를 감싸준다.
리덕스의 유의사항
1. 리듀서 함수를 통하지 않는 State 변경은 없다. 반드시, State 를 변경 할때에는 리듀서 함수를 사용한다.
2. 리듀서 함수 내에서, State 를 "직접 변경" 하지 않는다. 반드시, 새 객체에 담아, 변경한다.
3. 초기 State 를 지정했다면, 리듀서 함수내에서 return 할때, 변경하지 않더라도, 프로퍼티로 넣어준다.
return 하는 객체에 그 키 값이 없다면, 그 값은 undefined 가 될 것이다.
자 그럼 코드로 알아보자.
1. 기본적인 리덕스 코드
<store/index.js>
import { createStore } from "redux";
const initialState = { counter: 0, showCounter: false }; // 1. 초기값을 빼서 사용하는 편이 좋다.
const counterReducer = (state = initialState, action) => {
if (action.type === "INCREMENT") {
return {
counter: state.counter + 1,
showCounter : state.showCounter // 2. 반환되는 값은 "새 return 으로 전부 교체되므로" 사라지지 않기 위해서는, return 한다.
};
}
if (action.type === "DECREMENT") {
return {
counter: state.counter - 1,
showCounter : state.showCounter
};
}
if (action.type === "USERINPUT") {
return {
counter: state.counter + action.number,
showCounter : state.showCounter
};
}
if(action.type ==="TOGGLE_COUNTER"){
return{
counter : state.counter,
showCounter : !state.showCounter
}
}
return state;
};
const store = createStore(counterReducer)
export default store
** 새 return 은 절대로 "merge" 되지 않는다.
** 새 return 은 완전히 "overwrite" 될 뿐이다.
** 초기값을 지정하고, return 하지 않았다면 그 프로퍼티는 자동으로 "undefined" 가 된다.
<App.js>
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import store from "./store/index"; // store 의 값을 가져온다.
import "./index.css";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}> // store 는 Provider 의 공급값을 정해주는 키이다.
<App />
</Provider>
);
<컴포넌트>
import classes from "./Counter.module.css";
import { useSelector, useDispatch } from "react-redux";
const Counter = () => {
const counter = useSelector((state) => state.counter); // useSelector 로 state 를 선택한다.
const dispatch = useDispatch(); // useDispatch 로, dispatch 에 접근한다.
const toggleCounterHandler = () => {};
const incrementHandler = () => {
dispatch({ type: "INCREMENT" }); // dispatch 안에 액션 타입을 넣어, 리듀서함수를 사용한다.
};
const decrementHandler = () => {
dispatch({ type: "DECREMENT" });
};
return (
<main className={classes.counter}>
<h1>Redux Counter</h1>
<div className={classes.value}>{counter}</div>
<div>
<button onClick={incrementHandler}> 증가</button>
<button onClick={decrementHandler}> 감소 </button>
</div>
<button onClick={toggleCounterHandler}>Toggle Counter</button>
</main>
);
};
export default Counter;
2. 페이로드를 이용해, 유동적인 리듀스 함수 만들기
< /store/index.js >
import { createStore } from "redux";
const initialState = { counter: 0, showCounter: false };
const counterReducer = (state = initialState, action) => {
...
}
if (action.type === "USERINPUT") {
return {
counter: state.counter + action.number, // action 의 프로퍼티로 온다.
showCounter : state.showCounter
};
}
<컴포넌트>
import {useSelector, useDispatch} from "react-redux"
const Counter = () =>{
const counterState = useSelector((state)=> state.counter)
const counterDispatch = useDispatch()
const increase = () => {
counterDispatch({ type:"USERINPUT", number:5 }) // 페이로드를 이용하여, 추가 프로퍼티를 생성한다.
}
}
3.가장 중요한 유의사항, 절대 원본의 State를 변경하지 말 것
const initialState = {name : xx, age : yy}
const reducerFn = (state = initialState, action)=>{
if(action.type === "AGE_INCREMENT"){
state.age = state.age + 1 // 바로 이것! 원본을 그대로 건드려버리고
return{
name : state.name,
age : state.age // 다시 객체로 리턴하는 행위!
}
}
}
const store = createStore(reducerFn)
export default store
** "객체" 와 "배열" 은 "참조값임을 명심하자" state.age 를 변경한 순간, "원본의 state 는 변형된다."
** 또한, 리덕스에서 state 를 직접 변경할 경우, "state 가 동기화되지 않거나, 얘기치 않은 버그가 발생 할 수 있다."
그렇다면... 생각해보자
1. 만약 수십개의 프로퍼티를 가진 객체가, 수십개가 있다면..? 그걸 다룰 Action 이 수 십 가지라면...?
유지보수가 가능할까?
const {} from "redux"
const reducerFn = ()=>{
if(...){
return {
x1 : ..,
x2 : ..,
x3 : ..,
x4 : ..,
...
}
}
if(...){
return {
x1 : ..,
x2 : ..,
x3 : ..,
x4 : ..,
...
}
}
}
2. action 을 다룰때, 자동완성은 없으니, 오타라도 발생한다면?
여러명의 개발자가 동시에 작업하면, 겹칠 수 있는 Action type 은 어떻게...?
<store>
if(action.type === "ASDASDGDVXCV"){
return {
...
}
}
< 컴포넌트 >
dispatch({ type : "ASDASDGDVdXCV"}) // 오타
3. 혹시나 리듀서 함수를 만들때, State 원본을 건드리는 실수를 했다면?
등등 수많은 문제점이 있다.
그러므로, 우리는 Redux-Toolkit 으로 Redux 를 사용할 것이다.
'Redux, Redux-toolkit' 카테고리의 다른 글
6. Redux-ToolKit [ State 다루는 몇가지 팁들 ] (0) | 2022.12.17 |
---|---|
5. Redux-ToolKit [ Slice 분할하기 ] (0) | 2022.12.17 |
4. Redux-ToolKit [다중 슬라이스 사용해보기] (0) | 2022.12.17 |
3. Redux-ToolKit 의 이론과 기본 사용 (0) | 2022.12.17 |
1. Redux 에 관하여 (0) | 2022.12.17 |
댓글