가수면
[Redux] Redux 본문
리덕스 설치
yarn add redux react-redux
데브툴 설치
npm install redux-devtools-extension
// 저장소에 추가해야할 것
import { composeWithDevTools } from "redux-devtools-extension";
const store = createStore(rootReducer, composeWithDevTools());
리덕스 기본
폴더 구성
초기값, reducer 설정 (modules 폴더)
// src/modules/counter.js
// 초기 상태값 (useState와 비슷한 역할)
// 객체 형태가 아니어도 됨.
const initialState = {
number: 0,
};
// 리듀서 (setState와 비슷한 역할)
const counter = (state = initialState, action) => {
switch (action.type) {
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default counter;
counter모듈 store에 연결
configStore.js
import { createStore } from "redux";
import counter from "../modules/counter"; //연결
import { combineReducers } from "redux";
/*
1. createStore()
리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수) 입니다.
리덕스는 단일 스토어로 모든 상태 트리를 관리한다고 설명해 드렸죠?
리덕스를 사용할 시 creatorStore를 호출할 일은 한 번밖에 없을 거예요.
*/
/*
2. combineReducers()
리덕스는 action —> dispatch —> reducer 순으로 동작한다고 말씀드렸죠?
이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생합니다.
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/
// redux가 여러 개일 경우
const rootReducer = combineReducers({
counter: counter
todos: totos
});
const store = createStore(rootReducer);
export default store;
// redux가 한 개일 경우 (Import한 변수값과 일치시키기)
const store = createStore(counter);
export default store;
index.js
// 원래부터 있던 코드
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
// 우리가 추가할 코드
import store from "./redux/conifig/configStore";
import { Provider } from "react-redux";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
//App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣어줍니다.
<Provider store={store}>
<App />
</Provider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
확인 및 적용
App.js
// src/App.js
import React from "react";
import { useSelector } from "react-redux"; // import 해주세요.
const App = () => {
//useSelector (훅) => 프로젝트에서 존재하는 모든 redux module의 state를 가져와라 (컴포넌트에서 store를 조회할 수 있게 해줌)
const counterStore = useSelector((state) => state)
const number = useSelector((state) => state.counter.number)
console.log(counterStore);
console.log(number);
return <div></div>;
}
export default App;
Counter 기능 구현
작동 원리
view에서 액션 -> dispatch가 액션을 store로 운반 -> 리듀서가 액션에 상응하는 state를 변경 -> view 변경
(1) dispatch에서 액션 객체를 받아서 리듀서로 전달 (dispatch는 반드시 객체 형식만 받음. type의 value는 대문자로 작성)
useDispatch추가
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // import 해주세요.
const App = () => {
const dispatch = useDispatch()
return <div>
{/* dispatch의 변수는 객체 형식을 갖춰야 함 */}
<button onClick={() => dispatch({ type: 'PLUSE_ONE' })}>+1</button>
</div>;
}
export default App;
// src/modules/counter.js
const initialState = {
number: 0,
};
const counter = (state = initialState, action) => {
console.log(action); //{type: 'PLUSE_ONE'}
switch (action.type) {
case "PLUS_ONE":
return { number: state.number + 1 };
default:
return state;
}
};
export default counter;
App에 추가
const App = () => {
const dispatch = useDispatch()
const number = useSelector((state) => state.counter.number)
console.log(number)
return <div>
{number}
<button onClick={() => dispatch({ type: 'PLUS_ONE' })}>+1</button>
</div>;
}
export default App;
action creator
액션객체를 효율적으로 관리하기 위한 방법
아래와 같이 추가해줄 수 있음
// src/modules/counter.js
// 추가된 코드 👇 - 액션 value를 상수들로 만들어 줍니다. 보통 이렇게 한곳에 모여있습니다.
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";
// 추가된 코드 👇 - Action Creator를 만들어 줍니다.
//const로 선언된 값은 action에 dispatch가 붙은 값임
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;
// src/App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // import 해주세요.
import { minusOne, plusOne } from "./redux/modules/counter";
const App = () => {
const dispatch = useDispatch()
const number = useSelector((state) => state.counter.number)
return <div>
{number}
{/* dispatch의 변수는 객체 형식을 갖춰야 함 */}
<button onClick={() => dispatch(plusOne())}>+1</button>
<button onClick={() => dispatch(minusOne())}>-1</button>
</div>;
}
export default App;
payload
사용자가 입력한 값을 action 객체에 같이 담아서 보내주는 것
문제) 사용자가 입력한 숫자 더하고 빼주기
(1) App.js 수정
// src/App.js
import { useState } from "react";
const App = () => {
const [number, setNumber] = useState(0)
const onChangeHandler = event =>
setNumber(+event.target.value)
return <div>
<input type='number' onChange={onChangeHandler} />
<button>더하기</button>
<button>빼기</button>
</div>;
}
export default App;
(2) counter.js 수정 (주석처럼 적어야할 것들을 미리 정리해서 리스트업 해놓으면 작성하기 좋음)
// src/redux/modules/counter.js
// Action Value
const ADD_NUMER = 'ADD_NUMER'
const SUBTRACT_NUMBER = 'SUBTRACT_NUMBER'
// Action Creator
export const addNumber = payload => {
return {
type: ADD_NUMER,
payload //ES6 문법으로 인해 key와 값이 같으면 값 생략 가능. 원래 형태 => payload: payload
}
}
export const subtractNumber = payload => {
return {
type: SUBTRACT_NUMBER,
payload //ES6 문법으로 인해 key와 값이 같으면 값 생략 가능. 원래 형태 => payload: payload
}
}
// Initial State
const initialState = {
number: 0
}
// Reducer
const counter = (state = initialState, action) => {
switch (action.type) {
case ADD_NUMER:
return {
number: state.number + action.payload
}
case SUBTRACT_NUMBER:
return {
number: state.number - action.payload
}
default:
return state
}
}
// export default reducer
export default counter;
(3) App.js 다시 수정 (useSelector)
// src/App.js
import { useState } from "react";
import { useSelector } from "react-redux";
const App = () => {
const [number, setNumber] = useState(0)
const globalNumber = useSelector(state => state.counter.number)
const onChangeHandler = event =>
setNumber(+event.target.value)
return <div>
<input type='number' onChange={onChangeHandler} />
<button>더하기</button>
<button>빼기</button>
</div>;
}
export default App;
(4) dispatch 사용하고 action추가
// src/App.js
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addNumber, subtractNumber } from "./redux/modules/counter";
const App = () => {
const [number, setNumber] = useState(0)
const globalNumber = useSelector(state => state.counter.number)
const dispatch = useDispatch()
const onChangeHandler = event =>
setNumber(+event.target.value)
const onSubtractNumberHandler = () => {
dispatch(subtractNumber(number))
}
return <div>
{globalNumber}
<input type='number' onChange={onChangeHandler} />
<button onClick={() => dispatch(addNumber(number))}>더하기</button>
<button onClick={onSubtractNumberHandler}>빼기</button>
</div>;
}
export default App;
정리 (Ducks pattern 기반)
1. 프로젝트에서 redux 폴더 생성 후 그 안에 리덕스 설정(config)폴더와 state 그룹(modules)폴더를 생성.
2. 저장소(configStore.js)를 만들고 양식 복붙
import { createStore } from "redux";
import counter from "../modules/counter"; //모듈
import { combineReducers } from "redux";
const rootReducer = combineReducers({
counter: counter //모듈
});
const store = createStore(rootReducer);
export default store;
3. index.js에 리덕스 요소들을 import하고 <App>를 Provider로 감싸준다..
// 추가할 코드
import store from "./redux/conifig/configStore";
import { Provider } from "react-redux";
.
.
.
<Provider store={store}>
<App />
</Provider>
);
4. modules 폴더에 js파일 생성 / 목표로 한 action에 dispatch와 action creator를 붙여준 뒤 useDispatch로 리듀서에 연결
// Action Value
// Action Creator
// Initial State
// Reducer
// export default reducer
5. 초기값 설정 -> reducer 함수 작성
6. 사용할 컴포넌트에 useSelector를 import한다.
'React > 라이브러리' 카테고리의 다른 글
[Styled Components] Styled Components Global Style (0) | 2022.12.17 |
---|---|
[Redux] Redux ToolKit thunk (0) | 2022.12.10 |
[Redux] Redux ToolKit (0) | 2022.12.09 |
Router (0) | 2022.12.03 |
[Styled Components] Styled Components (0) | 2022.12.02 |