231122 TIL

thisisyjin·2023년 11월 21일
0

TIL 📝

목록 보기
100/113

Context API

state 관리 방법

  1. state, props 이용 (컴포넌트 간 통신)
  2. React Context 이용
  3. MobX
  4. Redux
  5. Recoil 등 ...

React Context

  • Component 트리의 깊이와 관계 없이 Props를 전달하지 않고도 state 사용 가능.
  • 전역 데이터를 관리하는데 사용됨.

React.createContext

  • context 객체 생성
  • Provider에서 현재 context 값을 읽음
const MyContext = React.createContext(defaultValue);
  • Provider <-> Consumer 구조

Context.Provider

<MyContext.Provider value={ }>
  <AComponent />
  <BComponent />
  <CComponent />
</MyContext.Provider>
  • A, B, C 컴포넌트 모두 다 컨텍스트를 구독 중이다.
  • 컨텍스트 값이 바뀌면, 컴포넌트를 다시 렌더링한다.

[참고] 변경 사항은 Object.is와 동일한 알고리즘을 사용하여 이전 값과 비교하여 결정됨.


useContext

const value = useContext(MyContext);

사용 예시

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light); // initialState 지정하여 생성

const App = () => {
  return (
    <ThemeContext.Provider value={themes.dark}> 
      <Toolbar />
    </ThemeContext.Provider>
  );
}

const Toolbar = () => {
  return (
    <div>
      <ThemedButton/>
    </div>
  );
}

const ThemedButton = () => {
  const theme = useContext(ThemeContext); // themes.dark
  return (
    <button style={{background: theme.background, color: theme.foreground}}>
      style button
    </button>)
}
  • 위 코드에서 InitialValue는 themes.light지만,
    Provider가 value props로 내려주고 있는 값이 themes.dark이므로
  • useContext(ThemeContext)는 themes.dark가 됨.

사이드 프로젝트

클라이언트

  • Pages
  1. 주문 페이지
  2. 요약 페이지
  3. 완료 페이지
  • Components

백엔드

  • node.js

create react app (프로젝트 생성)

  • 현재 폴더에 생성하려면?
$ npx create-react-app ./

pages 생성

/src/pages에 아래와 같이 각 페이지 폴더와 index.js 생성

const SummaryPage = () => {
  return <div>SummaryPage</div>;
};

export default SummaryPage;

Summary Page 생성

구현해야 할 기능

  • 주문 확인 체크박스 체크해야 [주문 확인] 버튼 클릭 가능.
  • form을 이용하여 구현 (input checkbox)
  1. checked state를 관리
  2. input onClick 핸들러 (e.target.checked로 설정)
  3. button disabled (checked가 false면 disabled)
import { useState } from 'react';

const SummaryPage = () => {
  const [checked, setChecked] = useState(false);

  return (
    <div>
      <form>
        <input
          type="checkbox"
          checked={checked}
          onClick={(e) => setChecked(e.target.checked)}
          id="confirm-checkbox"
        />{' '}
        <label htmlFor="confirm-checkbox">주문하려는 것을 확인하셨나요?</label>
        <br />
        <button disabled={!checked} type="submit">
          주문 확인
        </button>
      </form>
    </div>
  );
};

export default SummaryPage;

주문 페이지 UI 생성

  • OrderPage에 들어갈 컴포넌트인 Type 컴포넌트 생성.
  • /src/components/Type.jsx

App > OrderPage > Type

  • Type에서는 OrderType 이라는 props를 받아서 구분함
const Type = ({ orderType }) => {
  return (
    <>
      <h2>주문 종류</h2>
      <p> 하나의 가격</p>
      <p>총 가격:</p>
      <div
        style={{
          display: 'flex',
          flexDirection: orderType === 'options' && 'column',
        }}
      ></div>
    </>
  );
};

export default Type;
const OrderPage = () => {
  return (
    <div>
      <h1>Travel Products</h1>
      <div>
        <Type orderType="products" />
      </div>
      <div style={{ display: 'flex', marginTop: 20 }}>
        <div style={{ width: '50%' }}>
          <Type orderType="options" />
        </div>
        <div style={{ width: '50%' }}>
          <h2>Total Price</h2> <br />
          <button>주문</button>
        </div>
      </div>
    </div>
  );
};

export default OrderPage;

백엔드 코드 연결

  • 상품 이미지 가져오기
  • API 요청 (axios)
  • Type 컴포넌트에서 orderType은 'products' 또는 'options' 인데,
    orderType에 따라 api를 다르게 요청함.
  • items 라는 state에 api response.data를 저장함.
import { useEffect, useState } from 'react';
import axios from 'axios';
import Products from './Products';
import Options from './Options';

const Type = ({ orderType }) => {
  const [items, setItems] = useState([]);
  console.log(orderType);

  useEffect(() => {
    loadItems(orderType);
  }, [orderType]);

  const loadItems = async (orderType) => {
    try {
      const response = await axios.get(`http://localhost:4000/${orderType}`);
      setItems(response.data);
    } catch (error) {
      console.error(error);
    }
  };

  const ItemComponents = orderType === 'products' ? Products : Options;

  const optionItems = items.map((item) => (
    <ItemComponents
      key={item.name}
      name={item.name}
      imagePath={item.imagePath}
    />
  ));
  return <>{optionItems}</>;
};

export default Type;


Products UI 작성

  • ItemComponents(=Products)의 props 확인
const Products = ({ name, imagePath }) => {
  console.log('products : ', name, imagePath);
  return <>Products</>;
};

export default Products;

백엔드(서버) 이미지 접근

  • 예> /images/america.jpeg 의 경우
  • {백엔드 URL}/images/america.jpeg
  • 즉, localhost:4000/images/america.jpeg 로 접근하면 됨.
const Products = ({ name, imagePath }) => {
  console.log('products : ', name, imagePath);
  return (
    <div style={{ textAlign: 'center' }}>
      <img
        src={`http://localhost:4000/${imagePath}`}
        alt={`${name} product`}
        style={{ width: '75%' }}
      />
    </div>
  );
};

export default Products;

API 에러 처리

  • ErrorBanner 컴포넌트 생성
const ErrorBanner = ({ message }) => {
  let errorMessage = message || '에러입니다.';
  return <div style={{ backgroundColor: 'red' }}>{errorMessage}</div>;
};

export default ErrorBanner;
  • Type 컴포넌트 수정
import { useEffect, useState } from 'react';
import axios from 'axios';
import Products from './Products';
import Options from './Options';
import Error from './Error';

const Type = ({ orderType }) => {
  const [items, setItems] = useState([]);
  const [error, setError] = useState(false);

  useEffect(() => {
    loadItems(orderType);
  }, [orderType]);

  const loadItems = async (orderType) => {
    try {
      const response = await axios.get(`http://localhost:4000/${orderType}`);
      setItems(response.data);
    } catch (error) {
      setError(true); // 에러 처리
    }
  };

  const ItemComponents = orderType === 'products' ? Products : Options;

  const optionItems = items.map((item) => (
    <ItemComponents
      key={item.name}
      name={item.name}
      imagePath={item.imagePath}
    />
  ));
  return (
    <>
      {optionItems}
      {error && <Error />}
    </>
  );
};

export default Type;

profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글