리덕스에 뛰어들기

맛없는콩두유·2022년 9월 3일
0
post-thumbnail

핵심 리덕스 개념 탐색하기

npm init -y 

터미널 창에 입력을 하면 빨강색 네모 박스 부분이 생깁니다.

const redux = require("redux");

기존의 import 방식과 다르게 redux를 불러옵니다.

const counterReducer = (state , action) => {
	return{
    	...
    };
}

기본적인 redux의 구조이고

const store = redux.createStore(counterReducer);

redux를 저장할 저장소를 생성해야하고,

const counterSubscriber = () => {
  // 구독
  const latestState = store.getState(); //최신상태
  console.log(latestState);
};
store.subscribe(counterSubscriber);

구독자도 생성해 줍니다.

store.dispatch({ type: "increment" }); //dispatch는 액션을 발송하는 메서드

action을 발송하는 dispatch 메서드를 통해서 객체를 생성해주면
counterReducer가 재실행되는 것을 볼 수 있습니다.

const counterReducer = (state = { counter: 0 }, action) => {
  return {
    counter: state.counter + 1, //기존 상태에 1을더한 것
  };
};

기본적으로 counter의 기본 값을 정해줘야합니다. 여기서는 0 으로 state에 정해주었습니다.

ux-example> node redux-demo.js
{ counter: 1 }
{ counter: 2 }

터미널 창에서 counter가 두번 실행돼서 2가 되는 것을 볼 수 있습니다.

더많은 리덕스 기본사항

리듀서 내부에서 액션을 살펴보겠습니다. 보통 action은 state와 다른 실행을 하기위해서 작동합니다.

const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === "increment") {
    return {
      counter: state.counter + 1, //기존 상태에 1을더한 것
    };
  }
  return state;
};

내부를 바꿔서 if 조건문을 달고

store.dispatch({ type:"decrement"});

dispatch를 추가해줍니다.

 if (action.type === "decrement") {
    return {
      counter: state.counter - 1, //기존 상태에 1을뺸 것
    };
  }

그리고 리듀서 내부에 또 다른 if 조건문을 추가해주면

{ counter: 1 }
{ counter: 0 }

counter의 결과값이 잘 나오는 것이 확인이 됩니다.

새 프로젝트 준비하기

> npm install redux react-redux

리덕스는 자바스크립트 프로젝트에서 쓰는 것이기 떄문에 우리는 리액트에서 써야합니다. 그러기 위해서는 터미널에 pm install redux 뒤에 react-redux를 붙혀줘야 합니다!

리액트용 리덕스 스토어 만들기

store 폴더 만들겠습니다. index.js파일을 만들고 이제 여기에 스토어를 만들고, 리듀서를 만들겠습니다! 하지만 구독하지는 않겠습니다.

  • store/index.js
import { legacy_createStore as createStore } from "redux";

createStore를 사용하려면 이 구문이 현재 버전에서 필요합니다!

const counterReducer = (state = { counter: 0 }, action) => {
  if (action.type === "increment") {
    return {
      counter: state.counter + 1,
    };
  }
  if (action.type === "decrement") {
    return {
      counter: state.counter - 1,
    };
  }
  return state;
};

const store = createStore(counterReducer); 

저번 시간과 마찬가지로 카운트를 증가/감소 시키는 것을 만들었는데 다른 점이 있습니다

store.dispatch({ type: "increment" });
store.dispatch({ type: "decrement" });

저번 시간에는 내부에서 dispatch를 해서 store를 연결하였다면
이번 시간에는 내부에서 dispatch할 것이 아니라 리액트 앱과 리덕스 스토어를 연결하길 원합니다.

export default store;

를 통해서 App.js에 스토어와 연결할 수 있습니다.

스토어 제공하기

store/index.js에서 store를 App.js에 연결시키기 위해서는 상위 파일인 index.js로 갑니다.


App component를 Provider로 감싸줍니다!

리액트 컴포넌트에서 리덕스 데이터 사용하기

현재 App.js에 가면 Counter Component로 사용하는 것을 볼 수 있습니다.

  • Counter.js
import { useSelector } from "react-redux";

useSelector는 자동으로 상태의 일부를 선택하게 해준다

const counter = useSelector((state) => state.counter); 

Counter함수 안에 넣고 리액트 리덕스에 의해 자동으로 실행된다.

<div className={classes.value}>{counter}</div>

{counter}로 value를 집어넣으면

기본 state value인 0이 뜨는 것을 볼 수 있습니다. 그러면 이 데이터를 어떻게 바꿀 수 있을까요?

내부 컴포넌트에서 action을 Dispattch하기

  • counter.js
      <div>
        <button onClick={incrementHandler}>Increment</button>
        <button onClick={decrementHandler}>Decrement</button>
      </div>

먼저 button을 두 개 만들겠습니다.

import { useSelector, useDispatch } from "react-redux";

 const dispatch = useDispatch();

useDispatch를 import 해줍니다.

const incrementHandler = () => {
    dispatch({ type: "increment" });
  };
const decrementHandler = () => {
    dispatch({ type: "decrement" });
  };

호출 함수에 dispatch로 원하는 type을 넣어주고

onClick={incrementHandler}
onClick={decrementHandler}

button 속성에 넣어줍니다.

클릭했을 떄 숫자가 잘 바뀌는 것을 볼 수 있습니다.

작업에 페이로드 연결하기

  • Counter.js
   <button onClick={increaseHandler}>Increment by 5</button>

버튼을 새로 추가해 5씩 증가하는 버튼을 만들어 보겠습니다.

  • index.js
  if (action.type === "increase") {
    return {
      counter: state.counter + action.amount,
    };
  }

index.js에서 state.counter + 5를 하여서 하드코딩을 할 수 있지만 이 것은 적합하지 못합니다.

  • Counter.js
  const increaseHandler = () => {
    dispatch({ type: "increase", amount: 5 });
  };

action으로 amount라는 이름을 가진 것은 dispatch에서 같은 이름의 값으로 불러오는 것이 적합합니다.

버튼을 누르면 5씩 증가하는 것을 볼 수 있습니다.

여러 State 속성 작업하기

ToggleButton을 클릭했을 떄 카운터가 사라졌다가 나타났으면 좋겠습니다. 그러기 위해선 useState를 이용해보겠습니다!

  • Counter.js
 <button onClick={toggleCounterHandler}>Toggle Counter</button>
const toggleCounterHandler = () => {
    dispatch({ type: "toggle" });
  };
  • store/index.js
const initialState = { counter: 0, showCounter: true };
const counterReducer = (state = initialState, action) => {

  if (action.type === "increment") {
    return {
      counter: state.counter + 1,
      showCounter: state.showCounter, //기존의 showCounter를 벨류를 취할 것
    };
  }

...

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

showCounter: state.showCounter를 각 type에 넣어줌으로써 기존의 showCounter의 value를 집어넣주는 문구를 넣어줍니다.

그리고 type이 toggle일 때 반대의 showCounter를 해주면 됩니다!

  • counter.js
{show && <div className={classes.value}>{counter}</div>}

show가 true일 떄 counter가 보이게 동적으로 처리해줍니다.

toggle버튼 누른 후

counter가 사라지는 것을 볼 수 있습니다!

리덕스 State를 올바르게 사용하기

Redux로 작업을 할 때는 기존의 State를 절대 변경해서는 안됩니다! 이로 인해 버그인 예측 불가능한 동작이 발생할 수 있고 디버깅하는 것도 어려워 질 수도 있습니다.

 if (action.type === "increment") {
 	state.counter++;
    return {
      counter: state.counter,
      showCounter: state.showCounter, 
    };
  }

이렇게 return 전에 state.counter++; 기존의 state를 변경시켜서 return을 하게되면 안된다는 말이다.

  if (action.type === "increment") {
    return {
      counter: state.counter + 1,
      showCounter: state.showCounter, //기존의 showCounter를 벨류를 ㅊ튀할 것
    };
  }

counter: state.counter + 1, 처럼 새로운 객체나 배열을 만들서
return을 해줘야한다!

리덕스 도전과제 및 리덕스 툴킷 소개

더 쉽게 리덕스를 사용할 수 있는 방법이 있습니다! 바로 Redux toolkit라는 라이브러리가 있습니다!

State 슬라이스 추가하기

npm install @reduxjs/toolkit

터미널에서 추가해줍니다!
그리고 package.json에서 redux를 제거해주세요!

  • store/index.js
import { createSlice } from '@reduxjs/toolkit';
createSlice({
  name: "counter",
  initialState, //initialState: initialState
  reducers: {
    increment(state) {
      state.counter++;
    },
    decrement(state) {
      tate.counter++;
    },
    increase(state, action) {
      state.counter = state.counter + action.amount;
    },
    toggleCounter(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

기존에 사용할 수 없었던 state.counter++; 직접 변경하는 것이 가능합니다.

    increase(state, action) {
      state.counter = state.counter + action.amount;
    },

increase는원하는 값만큼 변경시켜야하기 떄문에 action을 받습니다.

리덕스 툴킷 State 연결하기

import { createSlice, configureStore } from "@reduxjs/toolkit";

configureStore 를 import해주고

const counterSlice = createSlice({
  name: "counter",
  initialState, //initialState: initialState
  reducers: {
    increment(state) {
      state.counter++;
    },
    decrement(state) {
      state.counter++;
    },
    increase(state, action) {
      state.counter = state.counter + action.amount;
    },
    toggleCounter(state) {
      state.showCounter = !state.showCounter;
    },
  },
});

이름을 붙혀 const counterSlice 전역변수로 설정해줍니다.

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

configureStore의 메인 리듀서로 counterSlice를 할당해줍니다!

리덕스 툴킷으로 마이그레이션

이제 action을 이용해 접근 해보겠습니다!

  • store/index.js
export const counterActions = counterSlice.actions 

리듀서안에 메서드와 같은 이름을 붙이면 자동으로 액션 생성자 액션 객체를 만든다

이렇게하고 액션이 필요한 컴포넌트로 가면 됩니다.

  • counter.js
import { counterActions } from "../store/index";

counterActions를 imnport 해주고

  const incrementHandler = () => {
    dispatch(counterActions.increment()); //전체 액션 객체가 자동으로 생성
  };
  const decrementHandler = () => {
    dispatch(counterActions.decrement());
  };
  const increaseHandler = () => {
    dispatch(counterActions.increase(5)); //{ type:IDENTIFIER payload: 5}
  };

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

dispatch()에 counterActions . 리덕스안에 메서드이름을 붙혀주고

특히, increaseHandler에서는 increase(5) 5매개변수를 넣고 이 매개변수 이름은 payload라는 변수명으로 고정되어있다.

다시 store/index.js로 돌아가서

  • store/index.js
    increase(state, action) {
      state.counter = state.counter + action.payload;
    },

state.counter = state.counter + action.amount;에서 action.payload;로 바꿔야한다.

다중 슬라이스 작업하기

  • App.js

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

  • store/index.js
const initialAuthState = {
  isAuthenticated: false,
};
const authSlice = createSlice({
  name: "authentication",
  initialState: initialAuthState,
  reducers: {
    login(state) {
      state.isAuthenticated = true;
    },
    logout(state) {
      state.isAuthenticated = false;
    },
  },
});

로그인 인증에 대한 것을 확인해주기위해 새로운 메서드를 만듭니다.

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

그리고 reducer:는 map형태로도 받을 수 있다고 했습니다. authSlice도 추가가 가능하고 이때 변수명을 각각 counter, auth로 진행해줍니다!

  • counter.js
const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector((state) => state.counter.counter); 
  const show = useSelector((state) => state.counter.showCounter);

그리고 리듀서에서 reducer의 저장소에 이름을 counter 라는 key명으로 저장했기 때문에
state.counter.counter/ state.counter.showCounter
counter를 꼭 붙여줘야합니다!

새 슬라이드에서 읽기 및 Dispatch하기

로그인 했을 떄만 메뉴버튼과 로그아웃 버튼이 보이게 만들어보겠습니다!

  • App.js
import { useSelector } from "react-redux";

const isAuth = useSelector((state) => state.auth.isAuthenticated);

...

   {!isAuth && <Auth />}
   {isAuth && <UserProfile />}
  • Header.js
import { useSelector, useDispatch } from "react-redux";
import { authActions } from "../store/index";

const dispatch = useDispatch();
const isAuth = useSelector((state) => state.auth.isAuthenticated);

...

{isAuth && (
        <nav>
          <ul>
            <li>
              <a href="/">My Products</a>
            </li>
            <li>
              <a href="/">My Sales</a>
            </li>
            <li>
              <button onClick={logoutHandler}>Logout</button>
            </li>
          </ul>
        </nav>
      )}
  • Auth.js
import { useDispatch } from "react-redux";
import { authActions } from "../store/index";
const dispatch = useDispatch();

const loginHandler = (e) => {
   e.preventDefault();

   dispatch(authActions.login());
};

...

       <form onSubmit={loginHandler}>
  • Header.js (로그아웃 버튼)
 const logoutHandler = () => {
    dispatch(authActions.logout());
  };
  
   <button onClick={logoutHandler}>Logout</button>

로그인을 누르면 메뉴가 나타나는 것을 볼 수 있다.

profile
하루하루 기록하기!

0개의 댓글