티스토리 뷰
Redux
➡️전역상태 관리 라이브러리
State는 2개로 구분지을 수 있다.
Local state = useState이용한 상태(컴포넌트 안에서 사용되는 State)
Global state = 전체적인 상태 (redux)
-- yarn 설치방법 --
yarn add redux react-redux
package.json에서 확인해보기
-- Redux 폴더이름 만들기 --
일반적으로 Redux를 사용할 폴더이름은 아래와 같이 만든다고 한다!
⬇️redux폴더 아래
⬇️modules폴더 아래
✅actions: 액선타입과 액션 생성자 함수를 정의
✅ reducers: 상태의 일부를 관리하는 리듀서 함수 포함
✅ selectores: 상태 트리에서 특정 부분을 선택하는 선택자 함수
✅ middleware: 사용자정의 미들웨어를 정의하는 파일
⬇️config폴더 아래( 스토어를 생성하고 미들웨어,리듀서와 같은 필요한 설정을 결합하는 파일 )
✅ configstore.js: 중앙 state 관리소 (확장자: js)
✅configStore.js 만드는 방법
// configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux"; //reducer를 한번에 묶는 API
const rootReducer = combineReducers({
modules에서 만드는 파일의 reducer를 여기에 저장
}); //메서드는 () 를하면 호출된다
const store = createStore(rootReducer);
export default store;
❗ ❗ ❗ ⭐⭐⭐⭐⭐modules에서 만드는 파일의 reducer를 여기에 저장 ⭐⭐⭐⭐⭐ ❗ ❗ ❗
사용하고자 하는 컴포넌트에서 import해서 사용할때 위에 중요하다고 한 부분에 값을 import해서 사용할 수 있음
✅만든 Store 전역으로 설정하는 방법
// index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { Provider } from "react-redux";
import store from "./redux/config/configStore";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);
reportWebVitals();
사용하고자 하는 파일을 Provider태그로 감싸고 store={store} 값을 전달!!!
❗Provider import 필요
❗ store import 필요
✅state 만드는 방법
const initialState = {
number: 0
}
const counter = ( state, action ) => {
switch(action.type) {
default:
return state;
}
}
export default counter;
-- ① 초기값 --
const initialState = {
number: 0
}
처음에 항상 초기값을 설정해 주어야 한다.
-- ② reducer 함수 --
⚠️ configStore.js 에 들어가야하는 부분 ⚠️
const counter = ( state, action ) => {
switch(action.type) {
default:
return state;
}
}
reducer함수를 만들어주어야 한다.
() 안에는 2개의 인자값을 받고인자값으로는 항상 state, action 이 들어간다.
const counter = ( state=initialState, action ) => { }
state에는 초기값으로 설정해준 값을 주고
switch (action.type) {
default:
return state;
}
action은 객체이며 type과 payload를가지고 있으며, key, value 형태이다.
✅만들어둔 reducer함수 Store에 저장하기
import { createStore } from "redux";
import { combineReducers } from "redux";
import counter from "../modules/counter";
import person from "../modules/user";
const rootReducer = combineReducers({
counter,
person: person,
});
const store = createStore(rootReducer);
export default store;
위에서 중요하다고 그렇게 강조했던 부분에 내가 만든 reducer함수가 들어가있다!!!
counter: counter인데 이름이 같으니 생략 가능!!!
✅ Store에 저장한reducer import해서 사용하기
import { useSelector } from "react-redux";
function App() {
const counter = useSelector((state) => {
return state.counter;
});
const person = useSelector((state) => {
return state.person;
});
const data = useSelector((state) => {
return state;
});
return <div>redux</div>;
}
useSelector라는 매서드를 이용해 Store에 저장한 reducer들을 가져올 수 있다.
사용하고자 하는 파일에서 import를 전혀 하지 않았어도 사용할 수 있다!!
🔥주의사항🔥
위 예시에서는 useSelector안에 여러개의 reducer를 가지고 있지만
useSeletor 안에는 1개의 reducer를 사용하는것이 좋은 방법이다.
이유:
useSelector는 얕은 평가를 하기 때문에
여러개의 reducer를 가지고 있는다면 불필요한 평가를 해야한다.
때문에 useSelector 안에서 가지는 reducer의 값은 1개만 넣어주자!
또 다른 시도:
처음에는 Store에 있는 값을 다 불러와서 필요한거 사용하면 안되나?
라는 생각이 들어서 return하는 부분에서 아래 코드처럼 사용해보았다.
return <div>너는 {store.person.name} 입니다.</div>;
이렇게 되면 store에 가지고 있는 많은 reducer를 통째로 들고 오는 것이 되기 때문에
해당 컴포넌트가 Store에 과한 의존성을 가지게 되며
① 성능에 매우 안좋을 수 있겠다.
② 가독성이 떨어질 수 있다. 어떤 reducer를 가져왔는지 명확하게 보여주고
return에서도 조금더 간단하게 쓸수 있다.
③ useSelector를 사용하는 이유는 해당 값에 변동이 없을때 얕은 평가를 하고
불필요한 리렌더링을 하지 않게하기 위해 사용하는 것인데
굳이 여러개의 reducer를 사용해서 성능을 저하시킬 필요가 전혀 없다.
④ 재사용성 부분에서도 store에 있는 특정 부분에만 의존하는 컴포넌트를 사용하는 것이
재사용성 부분에서 훨씬 좋다. (불필요한 얕은 복사를 할 이유가 없음.)
**여러가지 시도를 해보았는데 결론은 useSelector 안에는 1개의 reducer만 사용하자 ^^
분명 알게되어 다행이지만 시간을 너무 많이 썼으니 누군가 읽는다면 시간낭비 하지 마세요..🥲
✅ reducer에 action 값 주기
const initialState = {
name: "miju",
age: 10,
hobby: "game",
};
const person = (state = initialState, action) => {
switch (action.type) {
case "GET_AGE":
return {
age: state.age + 1,
};
default:
return state;
}
};
export default person;
초기값 중 변화를 주고 싶은 값을 사용해 return안에 값을 넣어주는데,
현재 초기값은 객체이므로 return { } 객체안에 필요한 action을 주면 된다.
action을 줄때에는 case "type이름" : return { key: stage.key action(ex. +1) } 로 주면 된다.
만약 객체가 아닌 단순 타입일 경우 return state action(ex. +1)
✅ reducer를 사용하는 곳에서 action 사용하기
import { useDispatch, useSelector } from "react-redux";
function App() {
const person = useSelector((state) => {
return state.person;
});
const changeAge = useDispatch();
return (
<>
<div>일년이 지나서 당신의 나이는 {person.age}</div>
<button
onClick={() => {
changeAge({
type: "GET_AGE",
});
}}
>
일년 나이먹기
</button>
</>
);
}
① useDispatch를 상수로 선언해준다.
② 사용하고자 하는 곳에서 useDispatch의 상수를 불러오고 (매서드이므로 당연히 상수() 괄호로 호출)
③ 초기값을 객체로 주었으므로 {}를 사용해 객체로 type을 넣어준다.
④ 만약 객체가 아닌경우는 {}를 이용해 type을 넣어줄 필요가 없다.
✅ reducer type을 상수로 만들어 주기
// user.js
export const GET_AGE = "GET_AGE";
export const OLD_AGE = "OLD_AGE";
const initialState = {
name: "miju",
age: 10,
hobby: "game",
};
const person = (state = initialState, action) => {
switch (action.type) {
case GET_AGE:
return {
age: state.age + 1,
};
case OLD_AGE:
return {
age: state.age - 1,
};
default:
return state;
}
};
case에서 type으로 사용할 이름을 const를 이용해 상수로 만들어주고
case 에서 type 이름을 상수로 변경해 준다. ( 그럼 문자열이 아닌 상수임으로 " " 이런게 필요 없음. )
밖에서도 상수를 사용할 것 이므로 export 를 앞에 붙여주어야 한다.
그리고
// App.js
import { useDispatch, useSelector } from "react-redux";
import { GET_AGE } from "./redux/modules/user";
function App() {
const person = useSelector((state) => {
return state.person;
});
const changeAge = useDispatch();
return (
<>
<div>일년이 지나서 당신의 나이는 {person.age}</div>
<button
onClick={() => {
changeAge({
type: "GET_AGE",
});
}}
>
일년 나이먹기
</button>
<button
onClick={() => {
changeAge({
type: GET_AGE,
});
}}
>
일년 나이 버리기
</button>
</>
);
}
export default App;
import { GET_AGE } from "./redux/modules/user";
reducer를 사용하는 컴포넌트에서 꼭 만들어준 type 상수를 import 해와야한다.
그리고 action을 사용하는 곳에서도 상수이므로 " " 필요 없음!
번외, 당연한소리하는중..ㅋㅋ
변수로 만들어도 쌉 가능
import { useDispatch, useSelector } from "react-redux";
import { GET_AGE } from "./redux/modules/user";
function App() {
const person = useSelector((state) => {
return state.person;
});
const changeAge = useDispatch();
const handleplusone = () => {
changeAge({
type: GET_AGE,
});
};
return (
<>
<div>일년이 지나서 당신의 나이는 {person.age}</div>
<button onClick={handleplusone}>일년 나이먹기</button>
<button
onClick={() => {
changeAge({
type: "OLD_AGE",
});
}}
>
일년 나이 버리기
</button>
</>
);
}
✅ reducer value(반환되는 action)를 상수로 만들어 주기
//user.js
export const GET_AGE = "GET_AGE";
export const OLD_AGE = "OLD_AGE";
export const userValue = () => {
return {
type: GET_AGE,
};
};
const initialState = {
name: "miju",
age: 10,
hobby: "game",
};
const person = (state = initialState, action) => {
switch (action.type) {
case GET_AGE:
return {
age: state.age + 1,
};
case OLD_AGE:
return {
age: state.age - 1,
};
default:
return state;
}
};
export default person;
리덕스에서 권장되는 패턴임으로 번거롭지만 해주자..ㅎㅎ
export const userValue = () => {
return {
type: GET_AGE,
};
};
만약 객체가 아니였다면 return GET_AGE;
//App.js
import { userValue } from "./redux/modules/user";
function App() {
const person = useSelector((state) => {
return state.person;
});
const changeAge = useDispatch();
const handleplusone = () => {
changeAge(userValue());
};
return (
<>
<div>일년이 지나서 당신의 나이는 {person.age}</div>
<button onClick={handleplusone}>일년 나이먹기</button>
<button
onClick={() => {
changeAge({
type: "OLD_AGE",
});
}}
>
일년 나이 버리기
</button>
</>
);
}
사용되는 곳에서 마찬가지고 import 필수
import { userValue } from "./redux/modules/user";
그리고 원래 action을 반환하던 부분에서 action을 함수로 만들어 import 해주었으니 actio이름() **괄호로 호출**
-- payload --
: 사용자로부터 입력받은 값이나 다른 소스에서 얻은 값을 reducer에 있는 action에 전달한다.
**payload를 전달받은 reducer는 action + payload 기반으로 새로운 상태를 계산하고 반환하게 된다!!
'TIL' 카테고리의 다른 글
2024. 01.31 FanLetter 만들면서 알게 된 것들 정리하기 (1) | 2024.01.31 |
---|---|
2024. 01. 28 날짜로만 디데이 구하기 (0) | 2024.01.29 |
2024. 01. 25 useEffect & useRef & 캐싱 (0) | 2024.01.25 |
2024. 01. 24 Styled Component 설치 및 사용방법(yarn) (0) | 2024.01.24 |
2024. 01. 22 생각해보기 (0) | 2024.01.22 |
- Total
- Today
- Yesterday
- 영화별점만들기
- Warning: Each child in a list should have a unique "key" prop.
- readme 역할
- Fetch와 Axios 의 장단점
- axios 사용하기
- readme 작성해야 하는 이유
- readme작성해보기
- 별점만들기
- axios 설치하기
- styled component 설치방법
- simple Icon 사용방법
- Warning: A component is changing an uncontrolled input to be controlled.
- readme 작성 방법
- 유효성검사 css
- styled component GlobalStyle 사용방법
- Warning: validateDOMNesting(...): <li> cannot appear as a descendant of <li>
- 에러모음집
- 영화 별점
- readme 이미지 추가 방법
- git cache
- axios instance 작성하기
- axios CRUD
- styled component 사용방법
- styled component 조건부 사용방법
- nextjs 토큰 만료처리하기
- 유효성검사
- axiosinstance 사용 시 토큰 사용 법
- 별점 색채우기
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |