React)Redux 세팅

미누도 개발한다·2021년 6월 3일
1

리액트

목록 보기
1/11

리덕스 개념 모식도

Redux vs Context Api

둘다 Store를 컴포넌트가 Subscribe 하여, Store의 데이터가 변하면 , 구독하는 컴포넌트들이 리렌더링 되는데 의외로 퍼포먼스 측면에서 차이가 있다고한다.
ContextApi를 사용하면 root 컴포넌트에서, Provider컴포넌트가 네스팅 되는데, 마치 콜백지옥을 보는 것 같이 복잡해질 수 있다.

결론은 깔끔하게 리덕스로 구현하자!

세팅하기

패키지 설치 :npm install redux react-redux

Store.js

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

const initialState = {
    counter:0,
    showCounter:true
};

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

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

    return state
}
const store = createStore(counterReducer);

export default store;

핵심이 되는 Store 객체이다.
커멘트 해놓은 리덕스 툴킷은, 이후에 여러가지 리듀서를 포함할 경우에 사용할 것이다.
맨처음 리듀서가 초기화될 때를 위해 initialState를 설정하고, 리듀서함수에서 return state 를 해주어야한다.
store 객체를 생성할때는 createStore() 매개변수로 , 만든 리듀서함수를 전달한다.
구독하는 컴포넌트에서 dispatch함수를 호출하면, 리듀서 함수가 실행될 것이다.

index.js

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

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

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

root 폴더의 index.js 에서 , App컴포넌트 전체를 Provider로 감싼다. App 컴포넌트, App컴포넌트 하위의 컴포넌트들이 store 데이터에 접근할 수 있다.
위에서 만든 store 객체를 Provider 컴포넌트에 연결시켜주자.


App.js

import React,{Fragment} from 'react';
import Counter from './components/Counter';


function App() {
  return (
    <Fragment>
      <Counter/>
    </Fragment>
  );
}

export default App;

구현할 기능은, Counter 컴포넌트에서 store에 접근하여 count값을 받아오고, dispatch함수를 통해 store를 업데이트 할것이다.

Counter.js

import {useSelector, useDispatch} from 'react-redux';
import classes from './Counter.module.css';

const Counter = () => {
  
  const counter = useSelector(state => state.counter); // redux store의 state의 데이터들중에 일부를 가져오게해줌, 해당 hook을씀으로써 자동 구독이됨.
  const toggle = useSelector(state => state.showCounter);

  const dispatch = useDispatch();

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

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

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

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

export default Counter;

useSelector 훅 을 통해 store 값을 받아올 수 있다.
주의할 점은,
store 의 state 중에, 구독하고싶은 데이터들을 하나씩 뽑아서 사용한다. useSelector 훅을 사용하면 해당 컴포넌트가 store를 구독할 수 있도록, 리덕스 라이브러리가 처리해준다.

Q. store 객체 에서 state의 불변성을 지켜주어야 하나요?

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

위 부분을 아래와 같이 바꿔도 기능상 동작은 한다.

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

반드시 새로운 state값을 리턴할때는 불변성을 유지해주어야한다.
리액트에서 비교알고리즘을 얕은비교(shallow)를 택했기 때문이다.
따라서 store의 나머지 데이터들도 함께 넣어서 리턴해주어야한다.
이런 작업이 귀찮아서 결국 리덕스 툴킷을 사용하게된다 (immer)

Q. 구독하지 않는 일부 store 데이터가 변하면, 컴포넌트가 리렌더링이 될까?

예를들어, store 의 state 값으로, counter,toggle 값이 있다고 가정하자.
A컴포넌트는 counter를 구독하고 (useSelector를 통해), B컴포넌트에는 toggle값만 구독하고 있고.
C컴포넌트는 counter,toggle 모두 구독하고있다고 가정하자.

toggle값만 변함 -> B컴포넌트,C컴포넌트 리렌더링
counter값만 변함 -> A컴포넌트,C컴포넌트 리렌더링

마무리...

훨씬 포스팅할 내용들은 많은데... 천천히 더 추가해야겠다

Git 👇
https://github.com/sae1013/react-redux-demo/tree/redux-basic/src

profile
빨리 가는 유일한 방법은 제대로 가는 것이다

0개의 댓글

관련 채용 정보