[Redux]리덕스를 사용하여 리액트 애플리케이션 상태 관리하기

Hyoyoung Kim·2024년 2월 25일
0

React TIL

목록 보기
38/40

리액트 프로넥트에서 리덕스를 사용할 때 가장 많이 사용하는 패턴프레젠테이셔널 컴포넌트컨테이너 컴포넌트를 분리하는 것이다.

  • 프레젠테이셔널 컴포넌트 : 주로 상태 관리가 이루어지지 않고, 그저 props를 받아와서 화면에 ui를 보여주기만 하는 컴포넌트
  • 컨테이너 컴포넌트 : 리덕스와 연동되어 있는 컴포넌트로, 리덕스로부터 상태를 받아오기도하고 리덕스 스토어에 액션을 디스패치하기도 한다.

✔️ 이러한 패턴은 필수는 아니지만 이런 패턴을 사용하면 코드의 재사용성도 높아지고, UI에 더 집중할 수 있다.

➡️ 카운터 컴포넌트 만들기

1. 카운터 컴포넌트 만들기

//components/Counter.js

import React from 'react';

const Counter =({number, onIncrease, onDecrease})=>{
    return(
        <div>
            <h1>{number}</h1>
            <div>
                <button onClick={onIncrease}>+1</button>
                <button onClick={onDecrease}>-1</button>
            </div>
        </div>
    )
}

export default Counter;


//App.js

import React from 'react';
import Counter from './components/Counter';

const App = () =>{
	return (
    	return (
    		<div>
      			<Counter number ={0}/>
      		</div>
    	)
    )
}

export default App;

2. 액션 타입 및 액션 함수 정의

  • 액션 타입은 대문자로 정의하고, 문자열 내용은 모듈 이름/액션이름 형식으로 작성
  • 모듈 이름을 넣음으로써 나중에 액션 이름이 충돌되지 않도록 함
//modules/counter.js

//액션타입
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';

// 액션함수
// 앞부분에 export 키워드 들어가서 추후 이 함수를 다른 파일에서 불러와 사용할 수 있음.
export const increase = ()=>({type:INCREASE})
export const decrease = ()=>({type:DECREASE})

3. 초기상태 및 리듀서 함수 만들기

// modules/counter.js

(...)
 //초기함수 
const initialState={
    number:0
}

//리듀서 함수 생성
function counter(state=initialState,action){
    switch(action.type){
        case INCREASE:
            return{
                number : state.number+1
            }
        case DECREASE:
            return{
                number:state.number-1
            }
        default:
            return state;
    }
}

export default counter;
//export는 여러개 보낼 수 있지만
//default는 하나만 내보낼 수 있다.

불러오는 방식

import counter from './counter';
import {increase, decrease} from './counter';
import counter, {increase,decrease} from './counter';

4. 루트 리듀서 만들기

만약 리듀서를 여러개 만들었을 경우, 애플리케이션에서 스토어는 하나만 존재해야하기에 여러개의 리듀서를 하나로 합쳐주어야 한다.

// modules/index.js

import {combineReducers} from 'redux';
import counter from './counter';
import todos from './todos';

const rootReducer = combineReducers({
    counter,
    todos,
})

export default rootReducer;

나중에 불러올 때

import rootReducer from './modules'

➡️ 리액트 애플리케이션에 리덕스 적용하기

5. 스토어 생성하고 Provider 컴포넌트를 사용하여 프로젝트에 리덕스 적용하기

reate-redux에서 제공하는 Provider 컴포넌트를 사용하여 store를 props로 전달해준다.

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import rootReducer from './modules';
import { legacy_createStore as createStore } from 'redux'
//reate-redux에서 제공하는 Provider 컴포넌트를 사용하여 store를 props로 전달해준다.
import { Provider } from 'react-redux';

const store = createStore(rootReducer)

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>
);

reportWebVitals();

6. Redux DevTools의 설치 및 적용

  • 크롬 웹 스토어에서 Redux DevTools를 검색하여 설치하기
  • 패키지 설치하기
$ yarn add redux-devtools-extension
  • 적용하기
//src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import rootReducer from './modules';
import { legacy_createStore as createStore } from 'redux'
import { Provider } from 'react-redux';
import { composeWithDevTools } from 'redux-devtools-extension'; //Redux DevTools 

//composeWithDevTools()===> Redux DevTools 적용
const store = createStore(rootReducer, composeWithDevTools())

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Provider store={store}>
    <App />
    </Provider>
  </React.StrictMode>
);


reportWebVitals();

  • 크롬 개발자 도구에서 Redux 탭을 열어 리덕스 스토어 내부의 상태르 확인할 수 있다.

➡️ 컨테이너 컴포넌트 만들기

7. dispatch 적용

//containers/CounterContainer.js
import React from 'react';
import Counter from '../components/Counter';

const CounterContainer=()=>{
  return <Counter/>
};
  
 export default CounterContainer;

방법1. connect 함수

  • 위 컴포넌트를 리덕스와 연동하려면 react-redux에서 제공하는 connect 함수를 사용해야 합니다.
connect(mapStateToProps, mapDispatchToProps)(연동할 컴포넌트)
  • mapStateToProps : 스토어의 상태를 컴포넌트의 props로 전달하는 것을 설정하는 함수입니다.
  • mapDispatchToProps : 액션 생성 함수를 컴포넌트의 props로 전달하기 위해 사용하는 함수입니다.

connect 함수 적용

//containers/CounterContainer.js

import React from 'react';
import Counter from '../componenets/Counter';
import { connect } from 'react-redux';
import { decrease, increase } from '../modules/counter';

const CounterContainer =({number, increase, decrease})=>{
    return <Counter number={number} onIncrease={increase} onDecrease={decrease}/>;
}

const mapStateToProps= state =>({
    //state를 파라미터로 받아오고, 이 값은 현재 스토어가 지니고 있는 상태를 가르킨다.
    number : state.counter.number,
});
// 비구조화 할당을 통해 counter를 분리하여 사용
//const mapStateToProps= ({counter})=>({
//    number : counter.number
//})

const mapDispatchToProps= dispatch =>({
    //mapDispatchToProps는 스토어의 내장함수 dispatch를 내장함수로 받아온다.
    increase: () =>{
        dispatch(increase())
    },
    decrease : () =>{
        dispatch(decrease())
    }
})

export default connect(mapStateToProps,mapDispatchToProps)(CounterContainer);
// src/App.js

// import './App.css';
import Todos from './componenets/Todos';
import CounterContainer from './containers/CounterContainer';

function App() {
  return (
    <div>
      <CounterContainer/>
    </div>
  );
}

export default App;

방법2. connect 함수 내부에 익명 함수 형태로 선언해도 된다.

//containers/CounterContainer.js


import React from 'react';
import Counter from '../componenets/Counter';
import { connect } from 'react-redux';
import { decrease, increase } from '../modules/counter';

const CounterContainer =({number, increase, decrease})=>{
    return <Counter number={number} onIncrease={increase} onDecrease={decrease}/>;
}



export default connect(
    state=>({
        number:state.counter.number,
    }),
    dispatch=>({
        increase:()=>dispatch(increase()),
      //increase:()=>{return dispatch(increase())},
        decrease:()=>dispatch(decrease())
    })
)(CounterContainer)

방법3. redux에서 제공하는 "bindActionCreators" 유틸 함수 사용하기

  • 컴포넌트에서 액션을 디스패치하기 위해 각 액션 생성 함수를 호출하고 dispatch로 감싸는 작업이 번거롭습니다. 액션 생성함수의 개수가 많다면 더욱 그렇습니다.
  • redux에서 제공하는 bindActionCreators 유틸 함수를 사용해서 간편하게 구현할 수 있습니다.
//containers/CounterContainer.js

import React from 'react';
import Counter from '../componenets/Counter';
import { connect } from 'react-redux';
import { decrease, increase } from '../modules/counter';
import { bindActionCreators } from 'redux';

const CounterContainer =({number, increase, decrease})=>{
    return <Counter number={number} onIncrease={increase} onDecrease={decrease}/>;
}



export default connect(
    state=>({
        number:state.counter.number,
    }),
    dispatch=>
    bindActionCreators(
        {
            increase,
            decrease
        },
        dispatch
    )
)(CounterContainer)

방법4. mapDispatchToProps에 해당하는 파라미터를 함수형태가 아닌 "액션생성함수로 이루어진 객체 형태"로 넣어주는 방식

두번째 파라미터를 아예 객체형태로 넣어주면 connect 함수가 내부적으로 bindActionCreators작업을 대신 해줍니다.

//containers/CounterContainer.js


import React from 'react';
import Counter from '../componenets/Counter';
import { connect } from 'react-redux';
import { decrease, increase } from '../modules/counter';

const CounterContainer =({number, increase, decrease})=>{
    return <Counter number={number} onIncrease={increase} onDecrease={decrease}/>;
}



export default connect(
    state=>({
        number:state.counter.number,
    }),
        {
            increase,
            decrease
        }
)(CounterContainer)

0개의 댓글