티스토리 뷰
yarn add redux react-redux
리덕스 프로젝트 패턴
프레젠테이셔널 컴포넌트와 컨테이너 컴포넌트를 분리하여 사용.
- 코드의 재사용성이 높아지고
- UI 또는 이벤트에 따라 특정 컴포넌트에 집중할 수 있다.
● 컨테이너 컴포넌트
(container/CounterContainer.jsx)
리덕스에서 값을 받아오거나 액션을 처리하는 컴포넌트
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import Counter from "../compoents/counter/Counter";
import { decrease, increase, setText } from "../modules/counter";
const CounterContainer = () => {
const dispatch = useDispatch();
const { number, text } = useSelector((state) => {
return {
number: state.counter.number,
text: state.counter.text
};
});
const onIncrease = useCallback(() => dispatch(increase()), [dispatch]);
const onDecrease = useCallback(() => dispatch(decrease()), [dispatch]);
const onSetText = useCallback(
(str) => {
dispatch(setText(str));
},
[dispatch]
);
return (
<Counter
number={number}
text={text}
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetText={onSetText}
/>
);
};
export default CounterContainer;
● 프레젠테이셔널 컴포넌트
(component/counter/counter.jsx)
상태관리가 이루어지지 않고 props를 받아서 UI를 보여주기만 하는 컴포넌트
import React, { useState } from "react";
const Counter = ({ number, text, onIncrease, onDecrease, onSetText }) => {
const [input, setInput] = useState("");
return (
<div>
<h1> count : {number}</h1>
<div>
<button onClick={onDecrease}>-1</button>
<button onClick={onIncrease}>+1</button>
</div>
<div>
<p> Text : {text}</p>
<input
type="text"
vlaue={input}
onChange={(e) => setInput(e.target.value)}
/>
<button onClick={() => onSetText(input)}>change text</button>
</div>
</div>
);
};
export default Counter;
리덕스 파일 구조
● 코드 종류별로 파일 분리
- actions, constants, reducer 3개의 디렉터리를 만들고 그 안에 기능별로 파일을 하나식 만드는 방식.
- 리덕스 공식 문서에 사용되는 기본 구조
- 코드를 종류에 따라 다른 파일에 작성하여 정리할 수 있어서 편리하지만, 새로운 액션을 만들 때마다 세 종류의 파일을 모두 수정해야 하기때문에 불편할 수 있다.
● 기능별로 파일 하나에 작성(Ducks패턴)
- 기능에 따라 액션타입, 액션생성합수, 리듀서 함수를 파일 하나에 몰아서 작성하는 방식.
- 주로 Ducks패턴으로 많이 사용됨
* 위의 방법은 리덕스 프로젝트에서 주로 사용되는 파일 구조로 정해진 디렉터리 구조는 없으므로 작성자에 따라 다르게 사용이 가능하다.
액션타입 정의
● 리듀서 함수
(modules/count.js)
- 리듀서 기본 코드
//액션타입 정의 '모듈이름/액션이름'
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
const SETTEXT = 'counter/SETTEXT';
//액션 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
export const setText = (str) => ({ type: SETTEXT, text : str });
//초기값 설정
const initialState = {
number: 0,
text : ''
};
//리듀서 함수
//처음 렌더링 시 state가 undeinfed이면 초기값으로 initialState가 적용됨
function counter(state = initialState, action) {
switch (action.type) {
case INCREASE:
return { ...state, number: state.number + 1 };
case DECREASE:
return { ...state, number: state.number - 1 };
case SETTEXT:
return { ...state, text : action.payload };
default:
return state;
}
}
export default counter;
- react-actions
createAction, handleActions으로 액션함수를 생성하고 액션을 처리하는 리듀서 함수를 간단하게 작성 가능
import { createAction, handleActions } from 'redux-actions';
//액션타입 정의 '모듈이름/액션이름'
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
const SETTEXT = 'counter/SETTEXT';
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
export const setText = createAction(SETTEXT, str => str);
const initialState = {
number: 0,
text: '',
};
const counter = handleActions(
{
[INCREASE]: (state, action) => ({
...state,
number: state.number + 1
}),
[DECREASE]: (state, action) => ({
...state,
number: state.number - 1
}),
[DECREASE]: (state, action) => ({
...state,
text: action.payload
}),
},
initialState,
);
export default counter;
● 루트 리듀서
(modules/index.js)
- store는 프로젝트에서 하나만 선언하여 사용하는 것이 규칙
- 기능별로 선언한 리듀서 함수들을 하나로 모아서 하나의 store로 사용할 수 있도록 한다.
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({
counter,
todos,
});
export default rootReducer;
● 스토어 만들기
(index.js)
- 루트 리듀서를 스토어로 선언
- 컴포넌트 내에서 스토어를 사용할 수 있도록 Provider로 App컴포넌트를 감사고 store를 props로 전달
(redux devTools : 크롬 개발도구에서 리덕스 스토어의 디버깅을 지원하는 도구. 크롬 확장프로그램 설치 후 이용가능)
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import {createStore} from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import App from './App';
import rootReducer from './modules';
const store = createStore(rootReducer, composeWithDevTools());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
● 컨테이너에서 리듀서 참조
(container/CounterContainer.jsx)
- connect 함수
import React from "react";
import { connect } from "react-redux";
import Counter from '../compoents/counter/Counter';
import { decrease, increase, setText } from "../modules/counter";
const CounterContainer = ({ number, text, increase, decrease, setText }) => {
return (
<Counter
number={number}
text={text}
onIncrease={increase}
onDecrease={decrease}
onSetText={setText}
/>
);
};
//리덕스 스토어 안의 상태를 컴포넌트의 props로 넘겨줌
//파라미터로 받은 state는 현재 스토어가 지니고 있는 상태
const mapStateToProps = (state) => ({
number: state.counter.number,
text : state.couner.text
});
//액션 생성 함수를 컴포넌트의 props로 넘겨줌
//내장함수 dispatch를 파라미터로 받음.
const mapDispatchToProps = (dispatch) => ({
increase: () => {
dispatch(increase());
},
decrease: () => {
dispatch(decrease());
},
setText: (str) => {
dispatch(setText(str));
}
});
//connect로 리덕스와 연동
export default connect(
mapStateToProps,
mapDispatchToProps
)(CounterContainer);
react-redux
- useSelector, useDispatch메서드가 리덕스의 데이터를 참조하고 이벤트를 실행하는 과정을 대신 처리
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import Counter from "../compoents/counter/Counter";
import { decrease, increase, setText } from "../modules/counter";
const CounterContainer = () => {
const dispatch = useDispatch();
const { number, text } = useSelector((state) => {
return {
number: state.counter.number,
text: state.counter.text
};
});
const onIncrease = useCallback(() => dispatch(increase()), [dispatch]);
const onDecrease = useCallback(() => dispatch(decrease()), [dispatch]);
const onSetText = useCallback(
(str) => {
dispatch(setText(str));
},
[dispatch]
);
return (
<Counter
number={number}
text={text}
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetText={onSetText}
/>
);
};
export default CounterContainer;
참조 :
리액트를 다루는 기술(김민준 / 길벗)
'SW프로그래밍 > React' 카테고리의 다른 글
[React] children으로 받은 Element에 Prop 추가하기 (0) | 2022.01.12 |
---|---|
[React] redux 라이브러리 (0) | 2021.05.13 |
[React] api 호출, Axios (1) | 2021.05.12 |
[react List] react-virtualized 스크롤 핸들링 (0) | 2021.04.29 |
[React]SPA , React-Router (0) | 2021.04.27 |
- Total
- Today
- Yesterday
- Redux
- date
- javascript
- Props
- JSP
- Spring
- script
- datePicker
- hooks
- React
- 함수형
- nodeJS
- paging
- module
- JSON
- JSX
- value
- webpack
- angular
- 클래스형
- hashmap
- html
- list
- java
- 스프링
- typescript
- ajax
- Progressbar
- input
- 리액트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |