React Redux

Noah·2022년 5월 19일
0

React

목록 보기
2/2
post-thumbnail

Redux

A state management system for cross-component or app-wide state

  • 우리가 상태, 즉 ! 우리의 애플리케이션을 변경하고 화면에 표시하는 데이터를 관리하도록 도와주는 상태 관리 라이브러리
  • 우리가 데이터를 다수의 컴포넌트나 심지어 앱 전체에서 관리하도록 도와준다
  • 유동적인 상태 관리 라이브러리

Redux 작동 방식

  • 중앙 데이터 저장소(store)은 하나뿐이다.
  • 컴포넌트는 저장소를 구독하고 변화를 감지한다.
  • 변화된 데이터를 가져와서 UI에 보여준다.
  • UI상 변화가 생기면 action 을 통해 reducer 순수 함수에 전달.
  • 순수 함수로만 state 변경이 가능하다.

Redux 사용하기

  • Redux 는 JavaScript(자바스트립트) 상태관리 라이브러리이다.
  • React 에서 작업을 하기 위해서는
  • npm install redux react-redux 설치 해야함
store - index.js
// 리덕스 로직을 저장할 위치, 리덕스와 리액트를 연결함!
import { createStore } from 'redux';

// 식별자 오타 방지를 위해 상수 생성 후 내보내기
export const INCREMENT = 'increment';

// 초기상태 ( 보기 좋게 상수 처리 )
const initialState = { counter: 0, showCounter: true };

// 리듀서 함수
// showCounter: 토글버튼을 클릭시 counter 보여지는 여부 결정
function counterReducer(state = initialState, action) {
  if (action.type === INCREMENT) {
    return {
      counter: state.counter + 1,
      showCounter: state.showCounter,
    };
  }
  if (action.type === 'increase') {
    return {
      counter: state.counter + action.amount,
      showCounter: state.showCounter,
    };
  }
  if (action.type === 'decrement') {
    return {
      counter: state.counter - 1,
      showCounter: state.showCounter,
    };
  }

  if (action.type === 'toggle') {
    return {
      counter: state.counter,
      showCounter: !state.showCounter,
    };
  }

  return state;
}

// 저장소 생성
const store = createStore(counterReducer);

export default store;

// 리덕스 스토어를 리액트에 제공을 해야한다 어떻게 ??
Counter.js

// 리액트 리덕스 팀이 만든 커스텀 훅 을 이용해서 데이터 가져오기
import { useSelector, useDispatch } from 'react-redux';
import classes from './Counter.module.css';

// 식별자 상수 받아오기
import { INCREMENT } from '../store/index';

// useSelector : 자동으로 상태의 일부를 선택하게 해준다
// 어떤 데이터를 스토어에서 추출할지 결정한다.
// 리액트 리덕스가 자동으로 서브스크립션(독자)를 설정한다.
// useState 대신 사용
// 만약 클래스형 컴포넌트면 connect 를 사용할 수 있다

// useDispatch : action을 다루는 훅!

const Counter = () => {
  // Redux store에 대한 action을 보냄
  const dispatch = useDispatch();
  // 리액트 리덕스에 의해 실행
  const counter = useSelector((state) => state.counter);
  const show = useSelector((state) => state.showCounter);

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

  // payload (전송되는 데이터) 연결
  const increaseHandler = () => {
    dispatch({ type: 'increase', amount: 5 });
  };

  const decrementHandler = () => {
    dispatch({ type: 'decrement' });
  };

  const toggleCounterHandler = () => {
    dispatch({ type: 'toggle' });
  };

  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
      {show && <div className={classes.value}>{counter}</div>}
      <div>
        <button onClick={incrementHandler}>Increment</button>
        <button onClick={increaseHandler}>Increment by 5</button>
        <button onClick={decrementHandler}>Decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;

React toolkit

  • 항상 새로운 상태 스냅숏을 반환해야 한다, 초기값을 변경하면 안된다
  • 애플리케이션 규모가 커질수록 문제가 발생 !
  1. action type
    • 식별자 오타로 에러
  2. 관리하는 데이터의 양
    • 가지고 있는 데이터와 상태가 많을수록, 상태 객체도 점점 커짐 !!
    • 즉, 많은 상태를 복사해야하는 경우가 생긴다.
    • 또한 그 모든 상태 속성을 유지하려면, 조건문 또는 함수가 늘어날 수 가 있다.
  3. 꼭 지켜줘야 하는 상태의 변경 불가성 (초기값!)
    • 특히 중첩된 객체 및 배열에 있어서 복잡한 데이터를 가지고 있을 때, 중첩된 데이터를 실수로 바꾸면서 망치기 쉽다

⇒ 근데 다 알 필요 없어요!! 우리에겐 Redux toolkit 가 있거든요

  • Redux toolkit : 리덕스 만든 사람이 만들었어요.
  • npm install @reduxjs/toolkit 설치
  • 설치하고 나면 package.json 에서 redux 삭제
  • 이미 Redux toolkit에 포함되어 있기때문

Redux toolkit 을 이용해서 Redux 를 간편 단순하게!

  • toolkit을 이용해서 코드를 변경함
  store - index.js
// 리덕스 로직을 저장할 위치, 리덕스와 리액트를 연결함!
import { createSlice, configureStore } from '@reduxjs/toolkit';
//createSlice >>> createReducer
// 한번에 몇 가지를 단순화 한다

// 식별자 오타 방지를 위해 상수 생성 후 내보내기
// export const INCREMENT = 'increment';

// 초기상태 ( 보기 좋게 상수 처리 )
const initialState = { counter: 0, showCounter: true };

// 함수를 이용해서 전역 상태의 slice를 미리 만들어야 한다. (초기상태 아래에 작성)
// 초기 상태는 변하지 않으면서, 상태를 변화 시킬수 있다.
const counterSlice = createSlice({
  // 상태마다 식별자가 필요함
  name: 'counter',
  // key: value 같아서 한번만 작성
  initialState,
  // reducers : 객체 혹은 맵 , 메서드를 추가한다.
  reducers: {
    increment(state) {
      // Redux toolkit은 내부적으로 immer 이라는 다른 패키지를 사용하는데,
      // 이런 코드를 감지하고 자동으로 원래 있는 상태를 복제 한다.
      // 그리고 새로운 상태 객체를 생성하고 모든 상태를 변경할 수 없게 유지하고
      // 우리가 변경한 상태는 변하지 않도록 오버 라이드 한다.
      state.counter++;
    },
    decrement(state) {
      state.counter--;
    },
    // payload 가 필요함 (추가 데이터가 필요하다)
    // action 을 listen 하는 reducer를 가질 수 있다.
    // action 이 추가 payload 및 데이터를 가지고 있다.
    // action을 매개변수로 이용해서 리듀서 함수 및 메서드로 사용 할 수 있다.
    increase(state, action) {
      state.counter = state.counter + action.payload;
    },
    toggleCounter(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

// 저장소 생성
// configureStore : 객체를 전달한다.
// 만약 리듀서가 여러개일 경우, 객체를 설정해서 그 객체 안에 원하는 대로 속성 이름을 정하고
// key 값은 마음대로 정하고 value 값을 원하는 리듀서 함수를 설정하면 된다.
// 예시 reducer: { counter: counterSlice.reducer },

const store = configureStore({
  reducer: counterSlice.reducer,
});

// 액션 메소드 액션 객체 생성을 해준다
// 리듀서 내의 이름만 같으면 알아서 자동으로 (완전 좋은데 ?)
// 내보내고 필요한 컴포넌트에서 받기만 하면 된다~ 개꿀~~
export const counterActios = counterSlice.actions;

export default store;
  counter.js
// 리액트 리덕스 팀이 만든 커스텀 훅 을 이용해서 데이터 가져오기
import { useSelector, useDispatch } from 'react-redux';

import { counterActios } from '../store/index';
import classes from './Counter.module.css';

// useSelector : 자동으로 상태의 일부를 선택하게 해준다
// 어떤 데이터를 스토어에서 추출할지 결정한다.
// 리액트 리덕스가 자동으로 서브스크립션(독자)를 설정한다.
// useState 대신 사용
// 만약 클래스형 컴포넌트면 connect 를 사용할 수 있다

// useDispatch : action을 다루는 훅!

const Counter = () => {
  // Redux store에 대한 action을 보냄
  const dispatch = useDispatch();
  // 리액트 리덕스에 의해 실행
  const counter = useSelector((state) => state.counter);
  const show = useSelector((state) => state.showCounter);

  const incrementHandler = () => {
    dispatch(counterActios.increment());
  };

  // payload (전송되는 데이터) 연결
  const increaseHandler = () => {
    // { type : SOME_UNIQUE_IDENTIFIER, playload: 5 }
    dispatch(counterActios.increase(5));
  };

  const decrementHandler = () => {
    dispatch(counterActios.decrement());
  };

  const toggleCounterHandler = () => {
    dispatch(counterActios.toggleCounter());
  };

  return (
    <main className={classes.counter}>
      <h1>Redux Counter</h1>
      {show && <div className={classes.value}>{counter}</div>}
      <div>
        <button onClick={incrementHandler}>Increment</button>
        <button onClick={increaseHandler}>Increment by 5</button>
        <button onClick={decrementHandler}>Decrement</button>
      </div>
      <button onClick={toggleCounterHandler}>Toggle Counter</button>
    </main>
  );
};

export default Counter;
profile
프론트엔드가 꿈인 코린이

0개의 댓글