본문 바로가기
Frontend/Redux

React에서 Redux 사용하기

by Forsaken Developer 2023. 2. 14.
728x90
728x90

React에서 Redux 사용하기

리액트에서 리덕스를 사용하기 위해서 redux 패키지를 설치하는데 redux만 설치하는 건 아니다.

리덕스는 리액트에서만 쓰는 게 아니고 어떤 자바스크립트 프로젝트에서도 사용될 수 있다.

리덕스는 리액트에 관해 알지도 못하고 리액트에 관심도 없다.

리덕스와 리액트 앱의 작업을 쉽게 하기 위해서 react-redux 패키지를 추가로 설치한다.

npm install redux react-redux

먼저 store 폴더를 만들고 store를 정의한다.

import { createStore } from 'redux';

const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === 'increment') {
    return {
      counter: state.counter + 1,
    };
  }

  if (action.type === 'decrement') {
    return {
      counter: state.counter - 1,
    };
  }

  return state;
};

const store = createStore(counterReducer);

export default store;

리액트 앱의 컴포넌트와 리덕스 스토어를 연결해야한다.

컴포넌트 중 대다수가 저장소에 액세스해야 하거나 앱 전체가

저장소에 액세스해야 한다면 최고 수준에서 store를 제공해야 한다.

index.js 파일에서 store를 App컴포넌트로 제공한다.

import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';

import App from './App';
import store from './store/index';

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

이제 App 컴포넌트나 다른 모든 하위 컴포넌트는 저장소에 접근할 수 있다.

데이터에 대한 구독을 설정할 수도 있고 액션을 발송할 수도 있다.

함수형 컴포넌트에서 리덕스 사용

리덕스 저장소와 그 안에 있는 데이터에 액세스하기 위해서는 eact-redux 팀이 만든 커스텀 훅useSelector를 import 한다.

이걸 호출하고 useSelector에 react-redux가 실행할 함수를 넣어준다.

큰 애플리케이션에는 많은 프로퍼티와 중첩된 객체와 배열이 있는 더 복잡한 상태를 가지기 때문에상태 객체 전체에서 아주 작은 일부분만 쉽게 잘라내는 게 아주 중요한데 useSelector로 가능하다.

useSelector를 사용할 때 react-redux는 컴포넌트를 위해 리덕스 저장소에 자동으로 구독을 설정한다.

컴포넌트는 리덕스 저장소에서 데이터가 변경될 때마다 자동으로 업데이트되고 컴포넌트 함수가 재실행되면서 최신 state를 받게 된다.

또한 컴포넌트를 제거하거나 어떤 이유에서든 DOM에서 제거되면 react-redux도 자동으로 구독을 해지한다.

 const counter = useSelector((state) => state.counter);

이제 actions를 보내기 위해 또 다른 Hook인 useDispatch를 import한다.

action은 type 속성이 있는 객체로 type에 대한 값은 Redux store reducer에서 사용하는 identifiers 중 하나이어야 한다.

const dispatch = useDispatch();
import { useSelector, useDispatch } from "react-redux";

const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector((state) => state.counter);

  const incrementHandler = () => {
    dispatch({ type: "INCREMENT" });
  };
  const decrementHandler = () => {
    dispatch({ type: "DECREMENT" });
  };

  return (
    <main>
      <h1>Redux Counter</h1>
      <div>{counter}</div>
      <div>
        <button onClick={incrementHandler}>Increment</button>
        <button onClick={decrementHandler}>Decrement</button>
      </div>
    </main>
  );
};

export default Counter;

클래스형 컴포넌트에서 리덕스 사용

함수형 컴포넌트에서는 훅을 이용하여 리덕스에 접근하였으나 클래스 기반 컴포넌트에서는 사용할 수 없다.

클래스형 컴포넌트를 리덕스에 연결하기 위해서 connect를 import한다.

물론 함수형 컴포넌트에서도 사용할 수 있지만 함수형 컴포넌트에서는 훅을 쓰는 게 더 편리하다.

컴포넌트를 export할 때 커넥트 함수를 실행하고 새 함수를 리턴한다.

그리고 리턴함수에서 카운터를 인자로 넣어 실행한다.

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

첫 번째 인자는 mapStateToProps로 useSelector와 같다.

state를 prop으로 전달한다.

key는 state명, value가 state인 객체를 리턴한다.

const mapStateToProps = (state) => {
  return {
    counter: state.counter,
  };
};

두 번째 인자는 mapDispatchToProps로 useDispatch와 같다.

디스패치 함수를 prop에 저장하는 것이다.

mapDispatchToProps 함수가 리덕스에 의해 실행되고 key가 함수명, value가 dispatch함수인 객체를 리턴한다.

const mapDispatchToProps = (dispatch) => {
  return {
    increment: () => dispatch({ type: "INCREMENT" }),
    decrement: () => dispatch({ type: "DECREMENT" }),
  };
};

커넥트를 사용할 때 react-redux가 구독을 설정하고 관리까지 할 것이다.

이는 useDispatch와 useSelector의 대안일 뿐이다.

import { Component } from "react";
import { connect } from "react-redux";

class Counter extends Component {
  incrementHandler() {
    this.props.increment();
  }
  decrementHandler() {
    this.props.decrement();
  }

  render() {
    return (
      <main>
        <h1>Redux Counter</h1>
        <div>{this.props.counter}</div>
        <div>
          <button onClick={this.incrementHandler.bind(this)}>Increment</button>
          <button onClick={this.decrementHandler.bind(this)}>Decrement</button>
        </div>
      </main>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    counter: state.counter,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    increment: () => dispatch({ type: "INCREMENT" }),
    decrement: () => dispatch({ type: "DECREMENT" }),
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

payload

action에 type외에도 payload 전달이 가능하다.

counter state를 amount 만큼씩 증가하는 액션을 추가할 수도 있다.

if (action.type === 'increase') {
    return {
      counter: state.counter + action.amount,
    };
  }
const increaseHandler = () => {
    dispatch({ type: 'increase', amount: 10 });
  };
728x90
반응형

'Frontend > Redux' 카테고리의 다른 글

Redux 사용 시 주의사항  (0) 2023.02.15
Redux 작동방식  (0) 2023.02.13
Redux의 개념과 사용하는 이유  (0) 2023.02.12

댓글