Redux

박영은·2023년 9월 18일
0

Redux

react의 상태관리 툴 중 하나 cf ) ContextAPI, Recoil, MobX, Jotai...

npm install redux   // 일반 설치
npm install react-redux   // react 사용시 설치
npm install @reduxjs/toolkit react-redux 
// redux toolkit = 기존 redux 개선버전 (뭄법 좀 더 쉬움)
// 기존 redux만 쓰면 redux-actions, redux-thunk 등 기능들을
// 설치해서 사용해야되는데 이 번거로움을 해결해줌
// 사용 방법 redux-saga와 약간 다르니까 다음에 더 찾아보기.

🌱 단방향으로 데이터가 흐르게 하는 Flux아키텍처 아이디어를 잘 구현함.

🍀 실행 순서

  1. Store에서는 다시 ReducerAction을 전달함.
  2. ReducerState를 가공해서 새로운 StateStore로 전달함.

☘️ Store, Reducer, Action, Dispatch 사용 방법 및 예시

// index.js
import { Provider } from "react-redux";
import { createStore } from "redux";

// action 객체를 리턴하는 함수 생성
const increase = () =>{
	return {
    	type : "INCREASE"
    };
};
const decrease = () =>{
	return {
    	type : "DECREASE"
    };
};

const count = 1;
// reducer 함수 생성
const countReducer = (state = count, action) => {
	switch (action.type){
      case "INCREASE" : 
        return state + 1;
      case "DECREASE" : 
        return state - 1;
      case "SET_NUMBER" :
        return action.payload;
      default :
        return state;
    }
}

const rootEle = document.getElementById("root");
const root = createRoot(rootEle);

//reducer함수 생성 후 createStore에 전달까지 해야 reducer함수를 통해 상태변경 준비 완료.
const store = createStore(countReducer) 


root.render(
	<Provider store={store}>  //전역관리를 위해 App 컴포넌트를 감싸줌.
		<App />
	</Provider>
)


🌱 Store : 상태 저장소

  • 모든 state 값을 저장
  • 리듀서(Reducer)함수를 인자로 받음.
  • 일반적인 방법으로 꺼내오거나 변경하는 것은 불가능, 정해진 방식을 통해서만 가져오거나 변경 가능.
    = Store 내부 상태값의 안정성을 유지하기 위함.

🌱 Reducer : 상태를 조작하는 함수

  • 순수함수여야 함. (외부 요인으로 인해 기대한 값이 아닌 엉뚱한 값으로 상태가 변경되는 일이 없어야하기 때문)

  • 초기 상태 값과 액션(Action)을 인자로 받아 액션에 조작할 상태(State)를 지정함.

  • 첫 번째, 두 번째 파라미터 각각 = (현재 state값, action(객체)) => {..

  • reducer가 여러개인 경우 = combineReducers 으로 해결
    - 기능에 따른 reducer를 나누어서 작성하면 코드를 유지보수하는데 효율적임.

// action.js
export const ADD_TODO = "ADD_TODO";
export const COMPLETE_TODO = " COMPLETE_TODO ";
export const SHOW_ALL = "SHOW_ALL";
export const SHOW_COMPLETE = "SHOW_COMPLETE";

// {type : ADD_TODO, text : '할일'}
export function addTodo(text) {
  return {
    type: ADD_TODO,
    text: text,
  };
}

//{type: COMPLETE_TODO,. index : 3}
export function completeTodo(index) {
  return {
    type: SHOW_ALL,
    index: index,
  };
}

//{type: COMPLETE_TODO,. index : 3}
export function showCompletel() {
  return {
    type: SHOW_COMPLETE,
  };
}
export function showAll() {
  return {
    type: SHOW_ALL,
  };
}


// reducer.js
// 여러개인 경우 예시
import { conbineReducers } from "redux";
import todos from "./todos"; // reducer 함수 1
import filter from "./filter"; // reducer 함수 2
  
const reducer = combineReducers({todos : todos, filter : filter });
// combineReducers를 이용하여 todos, filter 두 가지의 reducer를 결합함.
// 전달된 action의 type값에 따라 둘 중 해당하는 reducer 함수로 전달함.

export default reducer;




// ⚡️ action.type 값이 ADD_TODO 또는 COMPLETE_TODO 인경우 todos reducer가 동작
// todos.js
import { ADD_TODO, COMPLETE_TODO } from "./actions";

const initialState = [];
//  todosInitialState = [{text : '산책', done : false}, {text : '점심먹기', done : false}]
export default function todos(previousState = initialState, action) {
  if (action.type === ADD_TODO) {
    return [...previousState, { text: action.text, done: false }];
  }

  if (action.type === COMPLETE_TODO) {
    return previousState.todos.map((todo, index) => {
      if (index === action.index) {
        return { ...todo, done: true };
      }
      return todo;
    });
  }
  
  return previousState;
}





// ⚡️ action.type 값이 SHOW_ALL 또는 SHOW_COMPLETE 인경우 filter reducer가 동작
// filter.js
import { SHOW_ALL, SHOW_COMPLETE } from "./actions";

const initialState = "ALL";

export default function filter(previousState = initialState, action) {
  if (action.type === SHOW_COMPLETE) {
    return "COMPLETE";
  }

  if (action.type === SHOW_ALL) {
    return "ALL";
  }
  return previousState;
}


🌱 Action : store에 있는 state를 변경하기 위해 보내는 신호.

  • 어떤 동작을 하는지 명시해주는 역할. (대문자(CASE) or SnakeCase 작성)
  • 필수 key로 type 사용 (type에 따라 변화)
// action ex)
// payload가 필요 없는 경우             // payload 필요한 경우
const action = {					const action = {
	type : "INCREASE"					type : "INCREASE"
}									  	payload : 10 ;
									}
// 액션 생성자(Action Creater) = action 객체를 반환하는 함수
const addTodo = () => {
  return {
  	type : "ADD_TODO",
    data : {
    	id : 1, 
      	text : "Redux add todo"
    } 
  }

🌱 Dispatch : 액션을 전달하는 함수

  • ReducerAction 전달하는 함수.
    - dispatch의 인자로 action 객체 담아서.
// reducer 함수로 해당 객체 전달
dispatch({type:"INCREASE", payload:5});
// reducer 함수로 action 객체 전달
dispatch(action);
// 액션 생성자를 사용하여 전달하는 경우
dispatch(addTodo(3))

react-redux 라이브러리에서 action객체를 reducer에 전달하고 변경된 state의 변경사항을 다른 컴포넌트에게 알려주는 역할을 하는 hooks를 지원해줌.
💥 useDispatch : reducetaction 객체 전달.
💥 useSelector : 컴포넌트와 state연결해서 reduxstate에 접근할 수 있게 해주는 메서드.(state가져오기)

// dispatch 사용 예시
// App.js
import { useDispatch, useSelector } from "react-redux";
import { increase, decrease } from "./index"

const App = () => {
	// store 객체의 dispatch 역할
	const dispatch = useDispatch(); // store의 dispatch 함수사용.
	// store 객체의 getState() 역할
	const state = useSelector((state)=>state.countReducer); // store의 state값을 가져오는 역할

	const plusBtn = () => { dispatch(increase()) };
	const minusBtn = () => { dispatch(decrease()) };


	return (
	  <div>
      		<p>{`Count : ${state}`}</p>
	        <button onClick={plusBtn}> + </button>
	        <button onClick={minusBtn}> - </button>
	  </div>
	)
}

export default App;



🍃 리덕스 사용시 따라야 할 세 가지 원칙

  1. 리덕스를 사용하는 앱에는 하나의 Store만 존재할 것.
  2. State 직접 변경 불가
    - State를 변경하기 위해서는 Action이 dispatch되어야 할 것.
  3. Reducer는 '순수 함수'로 작성할 것.
    - 이 때문에 리덕스 미들웨어(Redux Middleware)가 필요함.


🌱 Redux Middleware

  • Redux의 기능을 확장하기 위한 수단.
  • dispatch 함수를 감싸는 역할을 수행함.

🌱 Middleware 실행 순서

  1. 여러 미들웨어는 서로 체이닝 되고, 최종적으로 체이닝된 함수가 storedispatch함수로 주입됨.
  2. dispatch 함수에 action 객체를 담아 호출하게 되면 맨 마지막에 감싸진 middleware가 action을 전달받아 작업을 수행하고, 작업을 마치면 다음 감싸진 middleware를 실행하는 동작을 반복함.
  3. 모든 middleware를 지나면 reduceraction이 전달되어 state가 업데이트 됨.

    1. 리덕스 프로미스 미들웨어 (Redux Promise Middleware)
    yarn add redux-promise-middleware
    : Promise 기반의 비동기 작업을 조금 더 편하게 해주는 middleware.
    : Payload로 전달된 객체가 만약 Promise 객체라면 통신에 대한 응답이 올 때까지 기다린 후 결과값을 reducer에게 전달함.
    : 비교적 코드가 단순 = 기본적인 비동기 또는 조건부로 작업을 수행해야 하는 경우 유용함.
    : 리덕스 프로미스 미들웨어를 사용하지 않으면 action 생성함수는 payload로 promise객체 자체를 실어 보내므로 reducerstate를 정상적으로 전달할 수 없음.


    2. 리덕스 썽크(Redux-Thunk)
    yarn add redux-thunk
    : 리덕스 창시자(댄 어쩌고..)가 만듦.
    : 가장 많이 사용되는 비동기 작업 미들웨어
    : 객체가 아닌, 동기 또는 비동기 작업을 수행할 수 있는 함수를 말함.
    : action 생성자가 반환하는 개체로는 처리하지 못했던 작업을 함수로 반환할 수 있게 됨.
    = 반환받은 함수를 통해 다양한 작업 가능해진게 장점.

    3. 리덕스 로거(Redux logger)
    yarn add redux-logger
    : reducer가 실행되기 전과 실행된 후를 log로 편리하게 확인+비교 가능.

    4. 리덕스 사가(Redux Saga)







출처1
출처2

profile
Front-end

0개의 댓글