Redux 정리

j(a)son·2022년 11월 18일
0

pure redux 는 생략!..

1. react-redux

기존 props drilling 문제점

export default function App() {
  const [number, setNumber] = useState(1);
  return (
    <div id="container">
      <h1>Root : {number}</h1>
      <div id="grid">
        <Left1 number={number}></Left1> // state를 props로 전달
        <Right1 setNumber={setNumber}></Right1> // setState를 props로 전달
      </div>
    </div>
  );
}

function Left1(props) {
  return (
    <div>
      <h1>Left1</h1> 
      <Left2 number={props.number}></Left2> // props 전달
    </div>
  );
}
function Left2(props) {
  return (
    <div>
      <h1>Left2</h1>
      <Left3 number={props.number}></Left3> // props 전달
    </div>
  );
}
function Left3(props) {
  return (
    <div>
      <h1>Left3 : {props.number}</h1> // 최종 전달 된 props
    </div>
  );
}
function Right1(props) {
  return (
    <div>
      <h1>Right1</h1>
      <Right2 number={props.number} setNumber={props.setNumber}> </Right2>
    </div>
  );
}
function Right2(props) {
  return (
    <div>
      <h1>Right2</h1>
      <Right3 number={props.number} setNumber={props.setNumber}></Right3>
    </div>
  );
}
function Right3(props) {
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
          props.setNumber((prev) => prev + 1);
        }}
      ></input>
    </div>
  );
}

위와 같이 Root(부모, 최상위) 컴포넌트에서 useState를 사용하여 state 생성 후 자식 컴포넌트에게 state를 전달하기 위해서는 props drilling이 필요하다. 매우 비효율 적이고 중첩된 컴포넌트의 개수가 많아 진다면 코딩의 실수 도 발 생할 수 있다.

또한, 현재 Left3에서만 state가 변경 되고 있지만, console을 찍어보면 Left1 , Left2에서도 리렌더링이 일어나고 있음.

react-redux를 사용한 전역 state 관리

<장점>
1. 코드의 간결성 증가, 복잡성 감소
2. 전역 state를 가져온 컴포넌트에서만 리렌더링 발생

import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

현재 createStore는 RTK의 configureStore를 사용하라고 권장되고 있지만, 학습을 위해 사용

  • Provider : store에 있는 state들을 어떤 컴포넌트에 제공할 것인지 정해주는 wrapper 컴포넌트
  • useSelector : 어떤 state를 선택해서 해당 컴포넌트에서 사용할 것인지 정해주는 함수
  • useDispatch : 해당 state 값을 변경 시킬 때 사용
// 리듀서 : store 안에 있는 state를 어떻게 바꿀 것인가
function reducer(state = { number : 1 }, action) {
  switch (action.type) {
    case 'PLUS':
      return {
	  	...state,
        number: state.number + 1,
    }
    default:
      return state;
  }
}


// 스토어 생성
const store = createStore(reducer);
Left1 code ...
Left2 code ...
function Left3() {
  // useSelector를 이용해서 number state를 사용하겠다고 명시
  const number = useSelector((state) => state.number);
  return (
    <div>
      <h1> Left3 : {number} </h1>
    </div>
  );
}
Right1 code ...
Right2 code ...
function Right3() {
  // useDispatch를 사용하여 특정 state의 값을 변경하겠다.
  const dispatch = useDispatch();
  return (
    <div>
      <h1>Right3</h1>
      <input
        type="button"
        value="+"
        onClick={() => {
    	  // dispatch 에는 action object를 넘겨주는 것!
          dispatch({ type: 'PLUS' }); // dispatch 가 reducer를 호출!!
        }}
      ></input>
    </div>
  );
}

다른 react-redux 코드

import { createStore } from 'redux';
import { Provider, useSelector, useDispatch } from 'react-redux';

function reducer(state, action) {
  switch (action.type) {
    case 'up':
      return {
        ...state,
        value: state.value + action.step, // action.step 적용
      };
    default:
      return state;
  }
}

const initialState = {value: 0}; // initialState 지정
// initialState를 store 생성 시 선언
const store = createStore(reducer, initialState);

function Counter() {
  // value state를 사용하겠다.
  const count = useSelector((state) => state.value);
  const dispatch = useDispatch();
  return (
    <div>
      <button
        onClick={() => {
    	  // dispatch 에는 action object를 넘겨주는 것!	
          dispatch({ type: 'up', step: 2 });
        }}
      >
        +
      </button>
      {count}
    </div>
  );
}

RTK 사용한 코드

// counterSlice.js

import { createSlice } from '@reduxjs/toolkit';

// slice 생성
const counterSlice = createSlice({
  name: 'counter',
  initialState: {value: 0},
  reducers: {
    up: (state, action) => {
      state.value = state.value + action.payload;
    }
  }
})

// 다른 파일에서 사용할 슬라이스나, action 함수 export 시키기
export default counterSlice;
export const { up } = counterSlice.actions;
// store.js

import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './counterSlice';

// store 생성
const store = configureStore({
  reducer: {
    counter: counterSlice.reducer
  }
})

// store export
export default store;
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
import { up } from './counterSlice';

function Counter() {
  // useSelector 사용하여 counter 의 value state를 사용하겠다.
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  return (
    <div>
      // dispatch 안에 action 함수인 up을 사용
      // up(2) 실행 시 action.payload로 2가 전달 됨
      <button onClick={() => dispatch(up(2))}>+</button>
      {count}
    </div>
  );
}

export default function App() {
  return (
    // Provider로 감싸줘야함!
    <Provider store={store}>
      <div>
        <Counter></Counter>
      </div>
    </Provider>
  );
}

추가) RTK - thunk 비동기 작업 처리

createAsyncThunk는 비동기 작업을 처리하는 action 을 만들어 준다.


동기적인 작업은 reducers를 사용, 비동기적인 작업은 extraReducers를 사용


추가) redux persist

Persist 사용법

profile
himynameisjason

0개의 댓글