리덕스 사용법리액트 기초/Redux2021. 7. 26. 15:44
Table of Contents
Redux?
덕스(ducks) 구조
- 보통 리덕스를 사용할 때는, 모양새대로 action, actionCreator, reducer를 분리해서 작성한다. 예를 들어 액션은 액션끼리, 액션생성함수는 액션생성함수끼리, 리듀서는 리듀서끼리 작성한다.
- 하지만, 덕스 구조는 모양새로 묶는 대신 기능으로 묶어서 작성한다. 예를 들어 같은 기능을 가진 action, actionCreator, reducer을 한 파일에 넣는 것이다.
- 덕스 구조의 예시는 아래와 같다
// widgets.js
// Actions
const LOAD = 'my-app/widgets/LOAD';
const CREATE = 'my-app/widgets/CREATE';
const UPDATE = 'my-app/widgets/UPDATE';
const REMOVE = 'my-app/widgets/REMOVE';
// Reducer
export default function reducer(state = {}, action = {}) {
switch (action.type) {
// do reducer stuff
default: return state;
}
}
// Action Creators
export function loadWidgets() {
return { type: LOAD };
}
export function createWidget(widget) {
return { type: CREATE, widget };
}
export function updateWidget(widget) {
return { type: UPDATE, widget };
}
export function removeWidget(widget) {
return { type: REMOVE, widget };
}
// side effects, only as applicable
// e.g. thunks, epics, etc
export function getWidget () {
return dispatch => get('/widget').then(widget => dispatch(updateWidget(widget)))
- Action Creator는 컴포넌트들에서 불러와서 사용하기 때문에 export 키워드를 붙여준다.
- export와 export default의 차이점은 export default는 임포트할 때 중괄호 없이 불러올 수 있고 export만 붙어 있는 것은 중괄호 안에 콤마(,)로 엮어서 가져올 수 있다.
- export default로 내보낼 수 있는 것은 파일당 하나이다!
- Reducer를 export default로 빼줄 것이기 때문에 Action Creator들은 export로 빼준다.
리덕스 설치
yarn add redux react-redux
리덕스 모듈 만들어보기
- src 폴더 아래에 redux라는 폴더를 만들고, 그 안에 modules라는 폴더를 만들자.
- modules 아래에 bucket.js라는 리덕스 모듈을 만들어보자.
- bucket.js는 버킷리스트를 LOAD, CREATE하는 2가지 상태 변화를 처리하는 리덕스 모듈이다.
- create 기능의 경우 ActionCreator에서 리턴하는 객체에서 타입뿐만 아니라 추가할 값이 필요하다!
완성된 리덕스 모듈
redux/modules/bucket.js
- Initial State, Action, Action Creator, Reducer를 정의해준다!
- reducer 함수의 state 파라미터는 reducer에서 마지막으로 리턴한 값이다!
- 만약 리턴했던 값이 없다면 initialState가 된다!!
// Action
const LOAD = 'bucket/LOAD';
const CREATE = 'bucket/CREATE';
// initialState
// 초기 상태값
const initialState = {list: ["치킨 먹기", "컴퓨터 게임하기", "여행 가기"]};
// Action Creators
// 컴포넌트에서 action을 dispatch 하면 파라미터로 데이터를 받아서 action 객체를 리턴한다.
// 리턴된 action 객체가 reducer의 파라미터로 들어가서 type을 보고 date를 처리한다
export const loadBucket = (bucket) => {
// 불러오는 기능은 어떤 데이터를 줄 필요가 없지만 아래와 모양새를 맞추기 위해 추가.
return {type: LOAD, bucket};
}
export const createBucket = (bucket) => {
// 타입뿐만 아니라 데이터도 필요하다.
//CreateBucket 같은 경우에는 추가할 값이 필요하다.
return {type: CREATE, bucket};
}
// Reducer
// 컴포넌트에서 action을 dispatch 하면 action creator에서 리턴한 action 객체의
// type과 데이터를 가지고 state를 변경한다
// state는 리듀서에서 마지막으로 리턴한 값이다!!
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
// do reducer stuff
case "bucket/LOAD":
return state;
case "bucket/CREATE":
return {...state, list: [...state.list, action.bucket]};
// 만약 useSelector로 데이터를 불러오면 마지막으로 리턴한 state를 불러온다
default:
return state;
}
}
※ 주의사항 ※
- 불변성 유지를 위해 state의 값을 변경하려면 원래 객체를 건들지 않고 스프레드 연산자를 사용해서 새 객체를 복사한 후 값을 수정해서 리턴한다!
return {...state, list: [...state.list, action.bucket]};
Store 만들기
- redux 폴더 아래에 configStore.js 파일을 만들고 스토어를 만들어보자.
- 한 프로젝트에는 스토어가 하나만 있어야 한다.또한, 스토어에는 리듀서가 하나만 있어야 한다.
- 스토어를 만들 때 첫번재로 만약 reducer가 여러개 있다면 그 reducer를 하나로 뭉쳐줘야 한다.
- combineReducers()를 사용해서 여러개의 reducer를 묶을 수 있다.
const rootReducer = combineReducers({a, b, c, ...});
- 그 다음 store에 reducer를 넣어주면 된다.
- createStore()를 이용해서 store를 만들 수 있다.
const store = createStore(rootReducer);
- 마지막으로 export를 해서 내보내줘서 다른 곳에서 불러다 쓸 수 있게 만들어야 한다.
export default store;
완성된 스토어
redux/configStore.js
- 위에서 리듀서를 bucket이라는 이름으로 가져왔으므로 리액트 컴포넌트에서 데이터를 가져올 때 state.bucket으로 가져오면 된다!
//configStore.js
import { createStore, combineReducers } from "redux";
//우리가 만든 리덕스 모듈의 리듀서
import bucket from './modules/bucket';
// root 리듀서를 만들어줍니다.
// 나중에 리듀서를 여러개 만들게 되면 여기에 하나씩 추가해주는 거예요!
const rootReducer = combineReducers({ bucket });
// 스토어를 만듭니다.
const store = createStore(rootReducer);
export default store;
리덕스와 리액트 프로젝트 연결하기
- 위의 과정처럼 store를 만들었으면 리액트 프로젝트와 연결을 해야 한다.
- index.js에서 연결할 스토어를 불러오고 리액트 프로젝트에 리덕스를 주입해줄 Provider를 불러와야 한다.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
// 리액트 프로젝트에 리덕스를 주입해줄 프로바이더를 불러옴!
import { Provider } from "react-redux";
// 연결할 스토어.
import store from "./redux/configStore";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
reportWebVitals();
함수형 컴포넌트에서 리덕스 데이터 사용하기
- 클래스형 컴포넌트에서 connect() 함수를 이용해서 리덕스 스토어와 컴포넌트를 연결했지만, 함수형 컴포넌트에서는 useSelector() 훅을 사용해서 store에 접근하여 변수를 가져올 수 있고 useDispatch() 훅을 통하여 직접 action creator들을 dispatch 시킬 수 있다.
- 클래스형 컴포넌트의 connect와 다른 점은 connect 함수를 사용하는 경우 해당 컨테이너의 부모 컴포넌트가 리렌더링 될 때 해당 컴포넌트의 props가 바뀌지 않았다면 리렌더링이 자동으로 방지되지만, hook은 그렇지 않기 때문에 React.memo를 사용해서 컴포넌트의 성능 최적화를 해야한다.
useSelector()
- 리덕스 스토어의 데이터(=state를) 조회할 수 있다.
- 리덕스 모듈 리듀서에서 마지막으로 리턴한 state를 가져온다!!
- 리듀서에서 default로 return한 값을 가져온다!!
- 사용하기 위해서는 "react-redux"의 useSelector를 import 해서 사용해야 한다.
- 사용법은 아래와 같다.
const [변수명] = useSelector(state => [리덕스 스토어 state]);
import React from "react";
import { useSelector, useDispatch } from "react-redux";
const App = () => {
//const [변수명] = useSelector(state => state.[리덕스 state]);
//store에 넣어준 리듀서 bucket의 리턴 값에서 key가 list인 값을 가져옴
const bucket_list = useSelector(state => state.bucket.list);
return (
<div>함수형 컴포넌트</div>
);
}
export default App;
- 리덕스 스토어 state는 스토어에서 가져온 reducer의 이름을 넣어주면 된다.
- 예를 들어 const store = createStore({bucket}) 이라면 [리덕스 스토어 state]에 bucket을 넣어서 접근할 수 있다!
const [변수명] = useSelector(state => state.[리덕스 스토어 state]);
useDispatch()
- 리덕스 모듈에서 생성한 action을 발생시킬 수 있다.
- 사용하기 위해서는 내가 만든 리덕스 모듈의 ActionCreator와 "react-redux"의 useDispatch를 import 해야 한다!
- useDispatch를 "react-redux"에서 import 한 후에 useDispatch()를 변수에 할당하고 할당한 변수의 인자에 내가 만든 리덕스 모듈의 ActionCreator를 넣어주면 된다.
const dispatch = useDispatch();
...
const stateChange = () =>{
dispatch([ActionCreator]);
}
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import {actioncreator} from 'redux 모듈'
const App = () => {
const dispatch = useDispatch();
return (
<div onClick={()=>{dispatch(actioncreator())}}>함수형 컴포넌트</div>
);
}
export default App;
예시
App.js
import React from "react";
import { useSelector, useDispatch } from "react-redux";
// actionCreator를 불러옴
import { createBucket } from "./redux/modules/bucket";
import { useEffect } from "react";
const App = () => {
//const [변수명] = useSelector(state => state.[리덕스 state]);
//store에 넣어준 리듀서 bucket의 리턴 값에서 key가 list인 값을 가져옴
//리듀서에서 마지막으로 리턴한 값을 가져온다!
const bucket_list = useSelector(state => state.bucket.list);
const dispatch = useDispatch();
useEffect(() => { console.log(bucket_list) }, [])
return (
<div>
<button onClick={() => { dispatch(createBucket("피자 먹기")) }}>state 추가</button>
{bucket_list.map((item, idx) => {
return (
<div key={idx}>
{item}
</div>
)
})}
</div>
)
}
export default App;
클래스형 컴포넌트에서 리덕스 데이터 사용하기
- connect 함수를 이용해서 리덕스 스토어와 컴포넌트를 연결해야 한다.
connect()
- connect()는 리덕스 스토어와 컴포넌트를 연결한다.
- 사용할 때 'react-redux'에서 import해야 하고 컴포넌트를 export할 때 mapStateToProps, mapDispatchToProps 같은 함수를 매개변수로 컴포넌트와 묶는다.
- connect로 컴포넌트와 리덕스 스토어를 묶는 예시는 아래와 같다.
import React from "react";
import connect from "react-redux";
// 스토어가 가진 상태 값을 props로 받아오기 위한 함수
const mapStateToProps = (state) => ({
...
});
// 액션 생성 함수를 props로 받아오기 위한 함수.
const mapDispatchToProps = (dispatch) => ({
...
});
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>Class형 컴포넌트</div>
);
}
}
// connect로 묶기
export default connect(mapStateToProps, mapDispatchToProps)(App);
mapStateToProps()
- 컴포넌트의 props에 스토어의 데이터(=state)를 전달한다.
- 사용법은 아래와 같다.
import connect from "react-redux";
import React from "react";
// 스토어가 가진 상태 값을 props로 받아오기 위한 함수
const mapStateToProps = (state) => ({
// [전달 받을 props 이름] : state.[리덕스 state]
//this.props.bucket_list로 접근 가능.
bucket_list: state.bucket.list;
});
const mapDispatchToProps = (dispatch) => ({
...
});
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>Class형 컴포넌트</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
- [리덕스 스토어 state]는 스토어에서 가져온 reducer의 이름을 넣어주면 된다.
- 예를 들어 const store = createStore({bucket}) 이라면 [리덕스 스토어 state]에 bucket을 넣어서 접근할 수 있다!
[전달 받을 props 이름] : state.[리덕스 스토어 state]
mapDispatchToProps()
- 컴포넌트의 props에 스토어의 데이터(=state)를 변경할 수 있는 ActionCreator 함수를 전달한다.
- 사용하기 위해서는 리덕스 모듈의 ActionCreator를 import 해야 한다!
- 사용법은 아래와 같다.
import React from "react";
import connect from "react-redux";
// ActionCreator를 import
import {actioncreator1, actioncreator2} from 'redux 모듈'
// 스토어가 가진 상태 값을 props로 받아오기 위한 함수
const mapStateToProps = (state) => ({
...
});
// 액션 생성 함수를 props로 받아오기 위한 함수.
const mapDispatchToProps = (dispatch) => ({
action1: () => {
dispatch(actioncreator1)
},
action2: () => {
dispatch(actioncreator2)
}
});
class App extends React.Component{
constructor(props){
super(props);
}
render(){
return(
{/*action1을 아래처럼 이벤트 호출시 발생하는 함수로 사용 할 수 있다*/}
<div onClick={()=>{action1();}}>Class형 컴포넌트</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
@덕구공 :: Duck9s'
주니어 개발자에욤
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!