Redux

Redux사용시 컴포넌트들을 props 없이 state 공유가능
useReducer와 비슷한 구조를 지님
redux에서는 객체를 action객체라고 말함
action객체를 reducer로 보내는 행위 = dispatch

Redux 설치방법

npm

npm i redux react-redux

yarn

yarn add redux react-redux

디자인 패턴?

파일구성을 어떻게 분리하고 관리를 할건지

Container Presenter 패턴

Container Presenter 패턴은 리액트 디자인 패턴 중 하나로, 기능과 UI를 컴포넌트 상으로 분리한 것
데이터를 처리하고, 받아오는 부분(기능)은 Container 컴포넌트에서 담당
데이터를 보여주는 부분(UI)은 Presenter 컴포넌트에서 담당

기존 패턴(정통적패턴)

  • 폴더구성
  • counter.js만들기
  1. constants -> counter.js
    어떤 action이 필요한지 만듦
  2. export const INC_COUNT = "INC_COUNT" export const DEC_COUNT = "DEC_COUNT"
  3. actions -> counter.js
    action 객체를 만듦
import { INC_COUNT, DEC_COUNT } from '../constants/counter'

export function incCount(diff) {
  return {
    type: INC_COUNT,
    payload: {diff}
  }
}

export function decCount(diff) {
  return {
    type: DEC_COUNT,
    payload: {diff}
  }
}
  1. reducers -> counter.js
    어떤 action들을 case로 넣을지 갖고오기
import { INC_COUNT, DEC_COUNT } from "../constants/counter";

//초기값설정
const initialState = {number: 0}

//state: 현재상태, action: 괄호안에 넣은 그 값
export default function counter(state= initialState, action) {
  switch (action.type) {
    case INC_COUNT:
      return {number: state.number + action.payload.diff} 
    case  DEC_COUNT:
      return {number: state.number - action.payload.diff}
    default:
      return state  
  }
}
  1. store -> counter.js(index.js로 파일 이름이 지어줘도됨)
    store는 한 앱에 하나만 있는게 규칙
    이제 App.js에서 Provider형태로 쓸 수 있게됨
import { legacy_createStore as createStore } from "redux";
import counter from '../reducers/counter'

const store = createStore(counter)

export default store
  1. App.js
import { Provider } from "react-redux";
import store from "./redux/store/counter";

function App() {
  return (
    <Provider store={store}>
      <div>앱</div>
    </Provider>
  );
}

export default App;
  1. 폴더 생성
  2. components -> Counter.jsx
//데이터를 보여주는 부분(UI)

import React from 'react'

function Counter(props) {

  const { number, onDecrease, onIncrease } = props

  return (
    <div>
      <p>{number}</p>
      <button onClick={onIncrease}>+2</button>
      <button onClick={onDecrease}>-2</button>
    </div>
  )
}

export default Counter
  1. container -> CounterContainers.jsx
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import Counter from '../components/Counter'
import { incCount, decCount } from '../redux/actions/counter'

function CounterContainers() {
  const dispatch = useDispatch()
  const number = useSelector((state) => state.number)

  const onIncrease = () => {
    dispatch(incCount(2))
  }

  const onDecrease = () => {
    dispatch(decCount(2))
  }

  return (
    <Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
  )
}

export default CounterContainers
  1. App.js
import CounterContainers from "./containers/CounterContainers";

    <Provider store={store}>
      <CounterContainers />
    </Provider>

ducks 패턴

분리된 파일을 합쳐서 하나의 파일에다가 모든 코드를 작성하는 것

ducks 규칙

  1. 반드시 리듀서 함수를 default export 해야 한다.
  2. 반드시 액션 생성 함수를 export 해야 한다.
  3. 반드시 접두사를 붙인 형태로 액션타입을 정의해야 한다.
  4. (선택) 액션 타입은 UPPER_SNAKE_CASE 형태로 이름을 짓고 export 할 수 있다.

ducks패턴 사용방법

  1. 파일구성
  2. modules -> counter.js
    해당 파일에 constants, actions, reducers내용 넣기

// constatns폴더. 액션타입
const INC_COUNT = "INC_COUNT"
const DEC_COUNT = "DEC_COUNT"

// actions폴더. 액션 생성 함수
export function incCount(diff) {
  return {
    type: INC_COUNT,
    payload: {diff}
  }
}

export function decCount(diff) {
  return {
    type: DEC_COUNT,
    payload: {diff}
  }
}

// reducers 폴더
//초기값
const initialState = {number: 0}
// 리듀서
export default function counter(state= initialState, action) {
  switch (action.type) {
    case INC_COUNT:
      return {number: state.number + action.payload.diff} 
    case  DEC_COUNT:
      return {number: state.number - action.payload.diff}
    default:
      return state  
  }
}

Redux Looger

redux로 실행되는 로직에 대해 콘솔창에 기록을 남겨주는 역할담당 => 리덕스 미들웨어
(store, action객체 다룰때의 과정을 남겨줌)

설치

yarn

  yarn add redux-logger --dev

npm

  npm i redux-logger --dev
  import { applyMiddleware } from "redux";  
  import logger from 'redux-logger'

  const store = createStore(rootReducer, applyMiddleware(logger))

chrome Redux DevTools 확장프로그램 사용방법

Redux DevTools 확장프로그램 추가

  import { applyMiddleware, compose, legacy_createStore as createStore } from "redux";

  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
  const store = createStore(rootReducer, composeEnhancers(applyMiddleware(logger)));

검사창에서 실시간 로깅 확인가능

  1. modules -> index.js
//store내용 넣기
import { applyMiddleware, legacy_createStore as createStore } from "redux";
import logger from 'redux-logger'
import counter from './counter'

const store = createStore(counter)

export default store
  • 만약 여러개의 리듀서가 있을경우
import { legacy_createStore as createStore } from "redux";
import counter from './counter'
import { combineReducers } from "redux";

//combine을 해줬기때문에
//state.counter -> counter 리듀서가 관리하는 상태에 접근
const rootReducer = combineReducers({
  counter,
})

const store = createStore(rootReducer, applyMiddleware(logger))

export default store
  1. App.js
import { Provider } from "react-redux";
import CounterContainers from "./containers/CounterContainers";
import store from "./modules";

function App() {
  return (
    <Provider store={store}>
      <CounterContainers />
    </Provider>
  );
}

export default App;
  1. containers -> CounterContainers
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import Counter from '../components/Counter'
import { incCount, decCount } from '../modules/counter'

function CounterContainers() {
  const dispatch = useDispatch()
  const number = useSelector((state) => state.number)

  const onIncrease = () => {
    dispatch(incCount(2))
  }

  const onDecrease = () => {
    dispatch(decCount(2))
  }

  return (
    <Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
  )
}

export default CounterContainers

1, 2, 3번까지 같은방식으로 생성 후 pages폴더 생성해서 구분하는방법
pages폴더는 components안에 구분해 놓은파일들이 들어가는 폴더

Redux toolkit 설치방법

  1. 버전확인
    "react": "^18.2.0", "react-dom": "^18.2.0" -> 두 버전이 18버전이상인지 확인

  2. npm

npm i @reduxjs/toolkit react-redux

yarn

yarn add @reduxjs/toolkit react-redux
  1. store.js파일생성
import { configureStore } from '@reduxjs/toolkit'

export default configureStore({
  reducer: { }
}) 
  1. index.js로 이동
import { Provider } from 'react-redux';
import store from '경로'

<Provider store={store}></Provider> //Provider로 감싸기

Redux store에 state 보관하는 방법


1.
import { createSlice } from '@reduxjs/toolkit'

2.
//useState와 비슷한용도. state하나를 slice라 부름
const 작명 = createSlice({
  name: 'state이름',
  initialState: '값'
})


3.
export default configureStore({
  reducer: {
    작명 : 작명.reducer
   }
}) 

Redux store에 state 꺼내는 방법

useSelector

store에 저장되있는 상태를 가져오기 위한 Hook

1. `state` 꺼낼 컴포넌트로 이동
import { useSelector } from 'react-redux';

2.
const selector = useSelector((state)=>{ return state })
// { return state }state는 모든state를 말함 
// 원하는 state만 출력가능 
// ex) {return state.작명한이름}
console.log(selector) //store에 등록한 정보가 넘어오는걸 확인가능

Redux store에 state 변경하는 방법

useDispatch

useDispatchdispatch 함수를 실행할 수 있도록 해주는 Hook

1. store.js로 이동
  `state` 변경 원하는 `state`에 아래와 같은 형식으로 작성해주기
  reducers: {
    함수명(state) {
      return 변경시켜주세요
    }
  }
2. export 해주기
  export const { 함수명 } = state함수명.actions
3. 
  import { useDispatch } from 'react-redux';
  import { 함수명 } from './../store';

  const dispatch = useDispatch()

  dispatch(state변경함수())
  <button onClick={ () =>{ dispatch( 함수명() ) }}>변경하기</button>

state가 object/array일 경우 변경하는 방법

state가 object/array일 경우 return 없이 직접 수정 가능
(그래서 값이 하나여도 object/array형태로 만들어서 사용하기도함)

  initialState: { name: 'Yu', age: 20 },
  reducers: {
    함수명(state) {
      state.name = 'Sunny'
    },

    함수명(state, action) {
      state.age += action.payload
    }
  }
  // 파라미터에 값을 넣어줘서 변경가능
  <button onClick={ () =>{ dispatch( 함수명(10) ) }}>변경하기</button>

'프론트엔드 > React' 카테고리의 다른 글

useNavigate, NavLink, Lazy Loading  (0) 2022.08.09
React router dom v6 (리액트 라우터 버전6)  (0) 2022.08.09

+ Recent posts