2024.02.02 TIL - 리액트 Context API, Redux(설치, 개념, 작동원리, action type, payload, action creator)

Innes·2024년 2월 2일
0

TIL(Today I Learned)

목록 보기
56/147
post-thumbnail

리액트 Context API 사용하기

  1. context API
  • 전역 데이터 관리
  • 불필요한 prop-drilling을 방지할 수 있음
  1. context API 필수 개념
    1) createContext : context 생성 (context 객체)
    2) Consumer : context 변화 감지 (데이터 받아오는 하위 컴포넌트를 지칭)
    3) Provider : context 전달 (상위 컴포넌트에 위치함)

  2. useContext() : 하위 컴포넌트에서 데이터를 받을 때 사용되는 API

  3. 사용 방법

1) FamilyContext 파일 생성 : src > context 폴더에 FamilyContext.js

// FamilyContext.js (context폴더에 있음)
import { createContext } from "react"

export const FamilyContext = createContext(null);
// null : 초기값
// FamilyContext : provider로 하위 컴포넌트에 주입
// -> 하위 컴포넌트 어디든지 내려주기 가능!

2) 전역 상태로 내려줄 상태들이 있는 파일에서, 태그들 가장 바깥쪽에서 provider로 감싸기!

// App.jsx에서 하위 컴포넌트들로 내려주고 싶은 경우
import { FamilyContext } from "...FamilyContext 있는 경로"

function App () {
  const name = '길동';
  const age = 100;
  
  return (
    <FamilyContext.Provider value={{	// ⭐️ value - 내려줄 정보들
      name: name,
      age: age,	// key value 이름 같으면 하나로 생략 가능
      }}>
          <div>
              <input />
          </div>
	</ FamilyContext.Provider>	⭐️
  )
}
  • Provider의 value : 객체를 인자로, 객체 안에는 내려줄 데이터들 들어 있음

3) 하위 컴포넌트에서 데이터 받아오는 예시 2가지

// 예시 1)
import { useContext } from "react" ⭐️
import { FamilyContext } from "...FamilyContext 있는 경로" ⭐️

function Father() {
	const { name, age } = useContext(FamilyContext); ⭐️
    // 구조분해할당 해서 가져오면 context.name 하지 않아도 됨
  
  ...생략
}

// 예시 2)
import { useContext } from "react" 
import { FamilyContext } from "...FamilyContext 있는 경로"

function Father() {
	const context = useContext(FamilyContext); ⭐️
    // 이방법으로 가져오면 context.name 이런식으로 붙여야함
  	
  	console.log(context.name);	⭐️ 
  	console.log(context.age);
  return {
  }
  
  ...생략
}
  • context API의 단점
    • 모든 consumer가 계속 리렌더링됨
    • state는 복잡한 상태 관리에는 영…

Redux 소개 및 사용법

1. Redux 란?

  • 전역 데이터 관리라는 점에서 context와 유사하지만, context는 state를 포함하는 개념(state는 복잡한 상태 관리에는 별로임)
  • state 개념을 다른걸로 바꾸기 위해 등장한게 useReducer
    -> context API + useReducer = Redux
  • consumer가 계속 리렌더링되는 현상 고침 + 복잡한 상태관리 할 수 있게 고침

2. 사용 방법

1) 설치하기

  • yarn add redux react-redux : redux, react-redux 설치하기

2) 폴더 구조 설정하기

  • redux 폴더구조 설정하기 : 이해보다는 그냥 받아들여야 하는 영역임....!

    • src > redux 폴더 생성
      • redux 관련 코드를 모두 몰아넣음
    • redux > config 폴더 생성
      • 리덕스 설정 관련 파일 전부
    • config > configStore.js 파일 생성
      • 중앙 state 관리소 -> 설정 코드 (.js)
    • redux > modules 폴더 생성
      • state의 그룹!

3) configStore.js 내용 넣기

// 중앙 데이터 관리소(store)를 설정하는 부분

import { createStore } from "redux";
import { combineReducers } from "redux";

const rootReducer = combineReducers({
// 리듀서들 들어오는 자리
});
const store = createStore(rootReducer);

export default store;

4) store 사용하기 위한 내용 index.js에 넣기

  • StricMode와 App.js 를 <Provider>로 감싸주기
    • Provider는 react-redux로 부터 import 필요
    • Provider는 store를 전역에 내려줄 것임 : store={store} 가 필요한 이유
import { Provider } from "react-redux";
import store from "./redux/config/configStore";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>	⭐️
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Provider>	⭐️
);

5) modules 폴더 안에 리듀서들(.js 파일들로 분리) 만들기

  • 예시) counter라는 리듀서를 counter.js에 만듦
// 초기 상태값(state)
const initialState = {
  number: 0,
};

// ⭐️ 리듀서 : 'state'의 변화를 일으키는 함수
// (state를 action의 type에 따라 변경하는 함수)

// input(리듀서의 매개변수) : state, action
// ⭐️ action : state의 상태를 어떻게 변경할지를 표현함 (객체임 / type, payload를 가짐)

const counter = (state = initialState, action) => {
  switch (action.type) {
    // action의 type에 따라서 작업을 수행할 것
    case "Plus_One":  
      // ⭐️ action type을 switch문으로 지정하는군! 
      // ('Plus_One' type을 Store에 던져주면 아래의 return을 실행함)
      return {
        number: state.number + 1
      }
      // console.log(state) // {number: 0} 이라는 객체임!
    case "Minus_One":
      return {
      	number: state.number -1
      }
    default:
      return state;
  }
};

export default counter;

6) store에 counter 가져와서 저장하기

  • configStore.js 파일에 counter import 해와서 리듀서 저장공간에 저장하기
import counter from "../modules/coutner"	⭐️

const rootReducer = combineReducers({
  counter: counter,	  ⭐️
  // 이름 같으면 하나는 생략 가능하기에 conter, 라고 해도 됨
})
const store = createStore(rootReducer);

export default store;

7) 전역 데이터가 필요한 파일 안에서, 중앙 store에 접근하여 counter의 값 읽어오기
=> redux hook 사용! useSelector((state)=>{})

  • useSelector의 콜백함수의 매개변수 : state (store에 있는 리듀서 전체를 지칭함)
import { useSelector } from "react-redux";

function App() {
  const counter = useSelector((state)=>{
  return state.counter;
  })
  
  console.log("counter", counter);	// count의 key, value 들어있는 객체 반환(사진 참고)
  
  return <div>현재 카운트 : {counter.number}</div>;
}

export default App;


3. redux 의 작동 원리

(이미지 출처: redux 공식 홈페이지)

  • Store : 중앙 데이터 관리소

    • Reducer : 데이터 보관, 제어(action의 type에 따라 데이터 변경)
    • State : 데이터들의 '상태' (제어 X)
  • UI : 작성하는 컴포넌트

  • Dispatch : action 객체(type, payload) 를 Store에 던져주기!

  • Dispatch 가져오기 : redux hook 사용 필요 (useDispatch({}))

    • useDispatch import 필요
    • useDispatch의 인자 : ⭐️ action 객체! (type, payload를 key로 하는 객체)
// App.jsx
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";

function App() {
  const dispatch = useDispatch();	⭐️
  
  const counter = useSelector((state)=>{
  return state.counter;
  })
  
  return(
  <>
    <div>현재 카운트 : {counter.number}</div>;
    <button
      onClick={()=>{
  		// +1 해주는 로직 : counter 리듀서에서 "Plus_One" 가져오기
  		dispatch({
  			type: 'Plus_One'})	
 			 // ⭐️ 'Plus_One' type의 action 을 수행해줘!
  			 // 기존값에 + 1 하는 type임)
  		}}
            >+1</button>
     <button 
       onClick={()=>{
  		 dispatch({
  			type: "Minus_One"
  			})
  	   }}>
	 -1</button>
  </>
  ) 
}

export default App;

4. action type의 value를 변수로 관리하자!

  • 만약 value 이름을 바꾸고 싶으면 conter.js & App.jsx 전부다 바꿔줘야 함
    -> 휴먼에러 발생 확률 증가!

  • reducer 파일 최 상단에 action value 변수로 만들기

export const PLUS_ONE = "Plus_One"
// -> 바꾸고 싶어지면 뒤에만 "counter/Plus_One"하면 다른건 다 안바꿔도 됨 

-> value 이름 바꿔주고 싶으면 변수 이름만 바꿔주면 한번에 다 바꿀 수 있음


5. action creator

  • action value를 return 하는 함수!
// reducer - counter.js

export const plusOne = () => {
return {
	type: PLUS_ONE
	}
}
// 컴포넌트 - App.jsx
import { plusOne } from "...counter.js 경로"
...생략

dispatch(plusOne()); ⭐️

6. action의 payload

  • 액션이 어떤 데이터를 전달하는지를 나타내는 부분
    (action type을 얼만큼, 몇번 등 실행할 것이냐!)

7. Ducks 패턴

  • Erik Rasmussen 이 제안한 패턴
  • redux는 사용자가 customize 하기 때문에 협업시 중구난방이 될 확률이 높음
    -> 모듈을 작성할 때 이 패턴을 지켜보자!

1) Reducer 함수를 export dafault 하기
2) Action creator 함수들 export 하기
3) Action type은 app/reducer/ACTION_TYPE 형태로 작성

=> 모듈 파일 1개에 Action Type, Action Creator, Reducer가 모두 존재하는 작성 방식!


8. Redux의 핵심 이해

이 디 액 타 페 써 스 !!

(이) UI에서 event 발생
(디) onClick에서 dispatch 실행(괄호 열고 닫아!)
(액) dispatch의 인자로 action 객체
(타, 페) action객체는 무적권 typepayload
(써) reducer로 ㄱㄱ
(-) action : type과 일치하는 기능을 payload만큼 실행
(스) 결국 Store 안에있는 state가 변경

profile
꾸준히 성장하는 우상향 개발자

0개의 댓글