useContext, Redux, Redux Toolkit 비교하기

유다송·2023년 7월 25일
0

React

목록 보기
10/14
post-thumbnail

useContext

  • React에서 제공하는 Hook 중 하나로 컴포넌트 간에 상태를 공유하기 위해 사용됨.
  • 중간 컴포넌트를 건너뛰고 상위 컴포넌트에서 하위 컴포넌트로 직접 데이터를 전달할 수 있다.
  • 주로 상위 컴포넌트에서 상태를 관리하므로 상태 업데이트의 범위가 제한적.
  • useContext는 작은 규모의 애플리케이션에서 상태 관리를 수행하기에 적합하다.

useRef 사용

import React, { useContext, createContext } from 'react';


const ThemeContext = createContext(); // context 생성

// 테마 설정 컴포넌트
function ThemeProvider({ children }) {
  const theme = 'light';
  
 
  return (
    <ThemeContext.Provider value={theme}>
      {children}
    </ThemeContext.Provider>
  );
}


function ThemedButton() { // 테마를 사용하는 하위 컴포넌트

  const theme = useContext(ThemeContext);   // useContext로 ThemeContext를 theme 변수에 저장

  return (
    <button style={{ background: theme === 'light' ? '#ffffff' : '#000000', color: theme === 'light' ? '#000000' : '#ffffff' }}>
      {theme === 'light' ? 'Light Theme' : 'Dark Theme'}
    </button>
  );
}

// 앱 컴포넌트
function App() {
  return (
    <ThemeProvider>
      <ThemedButton />
    </ThemeProvider>
  );
}

Redux

  • 전역 상태 관리를 제공하여 여러 컴포넌트 간에 데이터를 효율적으로 공유할 수 있도록 해준다
  • Redux는 상태의 변화를 관리하고, 상태가 변화할 때마다 컴포넌트들에게 적절한 업데이트를 제공하기 위해 액션(Action)과 리듀서(Reducer) 등의 개념을 사용한다.
  • 단일 저장소(store)에 상태를 저장하고 관리한다.

Action

  • Action은 Redux에서 상태 변화를 발생시키는 일종의 객체. Action 객체에는 상태를 어떻게 변경할지에 대한 정보가 담겨 있다.
  • Action은 애플리케이션에서 발생하는 어떤 사건이나 이벤트를 설명하는 type과, 필요에 따라 추가적인 payload가 포함된다.

Reducer

  • Reducer는 Redux에서 상태를 변경하는 순수 함수이다. 이전 state와 Action을 받아서 새로운 상태를 반환.
  • Reducer는 Action의 타입에 따라 상태를 어떻게 업데이트할지를 결정한다.
  • 기존의 state를 직접 수정하지 않고, 불변성을 유지해야 한다.
const initialState = {
  users: [],
  isFormSubmitted: false,
}

const userReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_USER':
      return {
        ...state,
        users: [...state.users, action.payload],
      }
    case 'SUBMIT_FORM':
      return {
        ...state,
        isFormSubmitted: true,
      }
    default:
      return state
  }
}

export default userReducer

store

  • 스토어는 Redux에서 state를 저장하는 객체. 애플리케이션의 전역 상태를 관리하고, Action에 따라 상태를 업데이트하며, Reducer를 호출하여 새로운 상태를 계산.
  • Redux에서는 단 하나의 스토어만 사용.
  • 애플리케이션의 현재 state를 조회할 수 있다.
  • dispatch 메서드를 사용하여 Action을 전달하고, Reducer를 호출하여 상태를 업데이트.
  • subscribe 메서드를 사용하여 상태가 변화할 때마다 등록된 콜백 함수를 호출.
  • store는 보통 Redux의 createStore 함수를 사용하여 생성.
import {createStore} from 'redux'
import {Provider} from 'react-redux'
import userReducer from './reducers'

const store = createStore(userReducer)

Provider

  • Provider 컴포넌트를 사용하여 React 애플리케이션에 store를 제공합니다. 이를 통해 하위 컴포넌트들이 store에 접근할 수 있게 된다.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store'; // 생성한 스토어

import App from './App';

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

Connect

  • react-redux의 connect 함수를 사용하여 React 컴포넌트를 store와 연결. 이를 통해 컴포넌트는 Redux 상태를 프로퍼티로 매핑하고, 액션을 디스패치하여 상태를 변경할 수 있다.
import React from 'react';
import { connect } from 'react-redux';
import { login } from './actions'; // 예시: login 액션

const LoginComponent = ({ user, login }) => {
  return (
    <div>
      <h2>Hello, {user.username}</h2>
      <button onClick={() => login('user123', 'password123')}>Login</button>
    </div>
  );
};

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

const mapDispatchToProps = {
  login
};

export default connect(mapStateToProps, mapDispatchToProps)(LoginComponent)

Redux에서의 데이터 흐름 정리

1. 컴포넌트에서 Aciton 발생

2. Aciton은 Reducer로 전달

3. Reducer는 Acitond의 타입을 기반으로 state를 업데이트

4. 변경된 state는 store에 저장

5. store의 state가 변화하면, 해당 변화를 감지하고 있는 컴포넌트들이 자동으로 리렌더링

Redux Toolkit

  • Redux Toolkit을 통해 Redux의 상태 관리를 더 쉽고 간편하게 만들어준다.
  • 내부적으로 immer 라이브러리를 사용하여 불변성을 유지한다. 이를 통해 상태를 더 직관적이고 간결하게 업데이트 가능.
  • 몇 가지 유용한 미들웨어를 기본으로 내장. 비동기 작업 처리를 위한 createAsyncThunk와 같은 유틸리티 함수들을 사용하여 비동기 작업을 간단하게 처리 가능.
  • 개발자 도구를 사용하여 액션과 상태의 변화를 실시간으로 모니터링하고 디버깅할 수 있다.
  • 일반적인 Redux 패턴을 사용하는데 필요한 기본적인 설정을 간소화한다.

createSlice

  • reateSlice 힘수를 사용하면 액션과 리듀서를 한 번에 생성할 수 있다.
import { createSlice, current } from "@reduxjs/toolkit";
import informCardList from "../pages/Inform/InformSection/db/informCardImg.json";

const initialState = {
  informCardLists: informCardList,
};

const informCardListSlice = createSlice({
  name: "informCardList",
  initialState: initialState,
  reducers: {
    setInformCardList(state, action) {
      state.informCardLists = action.payload;
    },
  },
});

export const { setInformCardList } = informCardListSlice.actions;
export default informCardListSlice.reducer;

컴포넌트에서 Redux Toolkit 사용

import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; 
// useSeloector : Redux 스토어의 state를 선택(select)하여 React 컴포넌트에서 사용할 수 있게 해주는 역할을 한다.
import { setInformCardList } from "../../services/InformCardListSlice";

export default function BookMark() {
    const { informCardLists } = useSelector((state) => state.informCardLists);
    let temp = JSON.parse(JSON.stringify(informCardLists))
    return (
    	<>
            <section className="inform_card_detail">
              <>
                {temp
                  .filter((x) => x.bookmark === true)
                  .map((x) => {
                    return (
                      <BookmarkCardContaner
                        key={x.id}
                        id={x.id}
                        imgSrc={x.imgSrc}
                        bookmark={x.bookmark}
                      />
                    );
                  })}
              </>
            </section>
          </>
    )
}


export function BookmarkCardContainer(props) {
    const dispatch = useDispatch()
    const [isBlue, setIsBlue] = useState(true);
    const { informCardLists } = useSelector((state) => state.informCardLists);
    

    const handdleBookmark = (e) => {
        e.preventDefault()
        setIsBlue(!isBlue)
        let temp = JSON.parse(JSON.stringify(informCardLists))
        temp[e.currentTarget.dataset.id].bookmark = !temp[e.currentTarget.dataset.id].bookmark
        dispatch(setInformCardList(temp)); // setInformCardList 액션을 디스패치하여 temp로 변경된 informCardLists 상태를 업데이트.
    };
    
    
    
  return (
    <>
      <svg
        onClick={handdleBookmark}
        data-id={props.id - 1}
        className="inform_bookmark_emozi"
        fill="none"
        width="24"
        height="24"
        viewBox="0 0 18 18"
      ></svg>
      /
    </>
  );
}

1개의 댓글

comment-user-thumbnail
2023년 7월 25일

좋은 정보 감사합니다

답글 달기