사용자 정의 태그, 즉 Component 를 만들어서 체계적이고 잘 정돈된 앱을 만들게 해주는 기술
State를 중앙에서 관리해서 데이터가 예기치 않게 변경되는 가능성을 낮춰주는 기술임. (비교적 큰 스케일의 프로젝트에서 상태관리를 위해 사용)
공통점: 모두 개발의 복잡성을 낮춰주는 기술로써, 함께 활용시 시너지효과를 얻을 수 있다.
WITHOUT REDUX
props 방식으로 컴포넌트간 데이터를 교환, depth가 깊어질 수록
코드가 복잡하고 길어지게 된다.WITH REDUX
단일 store에 state를 저장하는 방식으로 컴포넌트가 직접 store로부터
state를 전달받을 수 있다.
Object 형태로 dispatch() 인자로 전달한다.
초기 1회 따로 명시하지 않는다면 (state === undefined) 로 호출된다.
store.getState()형태로 참조된다.
-Dispatch(): action객체를 받아 reducer에 전달하며 reducer를 호출한다.
(이후 reducer를 호출하고 state 변경이 완료되면 subscribe()를 호출한다.)
-Subscribe(): 등록된 함수,컴포넌트들을 다시 호출(렌더링)
-getState(): store.getState() 형태로 state값을 참조할 수 있다.
- [UI]
사용자가 특정 기능을 동작시킨다- [EventHandler]
이벤트핸들러로 dispatch()에 action이 담긴 형태로 호출
action: { type: 'DEPOSIT', payload: 10 }- [dispatch]
전달받은 action을 state와 함께 reducer에 다시 전달하며 호출- [state]
reducer( initialState, action) 가 동작하여 특정 state를 반환
새로 변경된 state는 store를 통해서만 참조할 수 있다.
npm install @reduxjs/toolkit
>store.js
'src/store.js'
//store.js에서 store를 생성(반드시 reducer도 함께 정의해야함)
import {createStore} from 'redux';
export default createStore(function reducer(state,action){
if(state === undefined){
return {number:0}
}
if(action.type === 'INCREMENT'){
return {...state, number:state.number + action.size}
}
return state;
},window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
프레젠테이셔널 컴포넌트가 여전히 부품으로써의 역할을 할 수 있도록
(다른 프로젝트에서도 코드를 재사용가능 하도록)
리덕스코드를 분리시키는 과정
Presentational Component
를 감싸는 컨테이너 컴포넌트
(리덕스,스토어와 관련 처리를 맡는)를 만든다.부모 컴포넌트
는 Container Component
를 임포트해서 렌더링한다.Container Component
는 다시 Presentational Component
를 임포트해서 렌더링하고, 리덕스 관련 처리를 맡는다.Presentaional Component
는 UI를 담당하는 원본 컴포넌트.<App>
<AddNumberRoot>
<Anonymus> ('컨테이너컴포넌트', 리덕스 관련 작업을 이 컴포넌트가 처리)
<AddNumber/> (화면에 무언가 표시하는 데에 집중하는 'Presentaitonal Component' 가 됨)
<Anonymus/>
<AddNumberRoot/>
<App/>
react-redux 라이브러리에서 제공하는 API & 컴포넌트
connect(mapStateToProps,mapDispatchToProps)(Wrapped Component)
{Provider}
'src/index.js'
import {Provider} from 'react-redux';
//(리액트 리덕스 제공 컴포넌트이다=> App을 감싸줌)
import store from './store';
//store.js파일 임포트
ReactDOM.render(
<Provider store={store}> // store라는 props를 <Provider>에 제공하며 <App>을 감싸준다.
<App />
</Provider>
, document.getElementById('root'));
//이로써 App의 모든 하위 컴포넌트에 store를 전역으로 사용할 수 있다.
//=> 각 컴포넌트에 import store from "../store";를 해줄 필요가 없다.
Connect(mstp,mrtp)는 (Wrapped_Component)를 인자로 받는 함수를 리턴하고,
이 함수는 다시 Wrapped_Component를 래핑한 컨테이너컴포넌트를 리턴한다.
function mapReduxStateToReactProps(state){
return {
number:state.number. //위 함수는 객체를 리턴, number는 props_name
/* ~ <DisplayNumber number={this.state.number}/>
(presentational) */
}
/*Redux state를 React의 props로 mapping-연결 해주는 함수
Redux.store의 state값이 인자로 기본적으로 제공되고,
store의 state값이 변경될 때마다
mapReduxStateToProps가 호출되고, 이는 DisplayNumber(pr)에 number라는 props이름으로
{state.number}가 제공된다.
*/
즉,
store.subscribe(function(){
this.setState({number:store.getState().number);}.bind(this));
//코드와
render(){
return <DisplayNumber number={this.state.number}></DisplayNumber>
/*의 number props를 React의 Presentational 컴포넌트에 전달하는 코드를 합친 것이 위 메서드의 특징이다.
[Redux.store.state 바뀔 때마다 return을 해주는데? 리턴해주는 객체는 React(wrapping)컴포넌트의 Props로 전달된다.]*/
function mapDispatchToProps(dispatch){
// === function mapReduxDispatchToReactProps(dispatch)
return {
onClick: function(size){
dispatch({type: 'INCREMENT', size:size});
}
//dispatch(actio)이 담긴 이벤트핸들러를 객체에 메서드형식으로 리턴
}
export default connect(null, mapDispatchToProps)(AddNumber);
//Redux Dispatch를 React의 props로 mapping-연결 해주는 함
//cf.) 원래 방식대로의 코드
import AddNumber from '../components/AddNumber';
import React, { Component } from 'react';
import store from '../store';
export default class extends Component{
render(){
return <AddNumber onClick={function(size){
store.dispatch({type: 'INCREMENT', size: this.state.size});
}.bind(this)}></AddNumber>
}
}
공통점: 두 메서드 모두 객체를 return하며 프로퍼티네임이 Props_name이다.
사용이유 : <Container>에서 <Presentational>에 쉽게 props전달이 가능하다.
React Hooks의 등장으로 동일한 기능을 Hook으로 대체할 수 있고
CustomHook을 사용하는 것이 더욱 직관적이기 때문이다.
[1] 생활코딩 react-redux
[2] https://velog.io/@velopert/using-redux-in-2021
[3] https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0