유튜브 react 강의 중 발견해서, API 다루는 법에 익숙해질 겸 clone 해보았다.
cocktail API를 이용해 칵테일들을 불러온다.
search bar 존재 - 원하는 칵테일을 검색할 수 있다. (타이핑 시작하면 칵테일들이 검색되기 시작)
원하는 칵테일을 클릭 시 자세한 정보를 볼 수 있다.
Home, About, Cocktail, error 페이지 라우팅
로딩 화면 구현
리액트에서 src안 폴더 나눌때: 크게 page, components로 구분한다. (경우 따라 다른 폴더 추가)
useCallback : Returns a memoized callback.
useContext / useGlobalContext
조금 익숙해진것 같기도? 매번 불필요한 부분에 props를 전달하지 않아도 state를 가져올 수 있다
redux와 useContext의 차이점
항상 헷갈렸는데 잘 설명해준 페이지를 찾았다!
useContext | Redux |
---|---|
hook | 상태 관리 라이브러리 |
data 공유 | data,state 관리 |
Context value에 의해 변한다 | pure function(리듀서들)에 의해 변한다 |
state를 바꿀수 o | state는 read-only, 직접 바꿀 수 x |
provider의 prop이 바뀔 시 모든 컴포넌트들이 다시 렌더링 된다 | 업데이트되는 컴포넌트만 다시 렌더링 |
작은 어플리케이션에 적합 | 큰 어플리케이션에 적합 |
이해하기 쉽고 적은 코드 | 이해하기 복잡 |
fetch 이용 칵테일 data 받아오는 프로세스
import React, { useState, useContext, useEffect } from "react";
import { useCallback } from "react";
const url = "https://www.thecocktaildb.com/api/json/v1/1/search.php?s=";
const AppContext = React.createContext();
const AppProvider = ({ children }) => {
const [loading, setLoading] = useState(false);
const [searchTerm, setSearchTerm] = useState("a");
const [cocktails, setCocktails] = useState([]);
const fetchDrinks = useCallback(async () => {
setLoading(true);
try {
const response = await fetch(
`${url}${searchTerm}`
); /* 이렇게 url 페치 가능!(searchTerm은 input값!) 신기하당 */
const data = await response.json();
const { drinks } = data;
if (drinks) {
const newCocktails = drinks.map((item) => {
const {
idDrink,
strDrink,
strDrinkThumb,
strAlcoholic,
strGlass
} = item;
return {
id: idDrink,
name: strDrink,
image: strDrinkThumb,
info: strAlcoholic,
glass: strGlass
};
});
setCocktails(newCocktails);
} else {
setCocktails([]);
}
setLoading(false);
} catch (error) {
console.log(error);
setLoading(false);
}
}, [searchTerm]);
useEffect(() => {
fetchDrinks();
}, [searchTerm, fetchDrinks]);
/* 받은 응답 변수로 지정해서 그 속의 프로퍼티를 빼냄, if조건문 안에서 map해서 그 안의
값들 또 꺼냄!(콘솔에 찍어서 프로퍼티 확인)
id 및 등등 객체로 저장해서 리턴!(맵이므로 저절로 배열로 나옴) -> 이것을 set~으로 상태변경!
없으면 빈배열! either way you have to change the Loding state one you've done */
return (
<AppContext.Provider
value={{
loading,
cocktails,
setSearchTerm
}}
>
{children}
</AppContext.Provider>
);
};
// make sure use
export const useGlobalContext = () => {
return useContext(AppContext);
};
export { AppContext, AppProvider };
{cocktails.map((item) => {
return (
<Cocktail key={item.id} {...item} />
); /* !!!!!이렇게 스프레드로 다 전달가능!!! */
})}
--------------------------------------------------------
<Link to={`/cocktail/${id}`} className="btn btn-primary btn-details">
details
</Link>
/* !!이렇게 받은 id prop을 링크할수 있다 - 클릭하면 해당의 세부페이지로 이동!!*/
useParams()
useParams returns an object of key/value pairs of URL parameters.
import { useParams } from 'react-router-dom';
export default function Profile() {
const { username } = useParams(); // 보통 이렇게 구조분해로 꺼내고, rest API 등 이용
const [user, setUser] = useState(null);
const history = useHistory();
useEffect(() => {
async function checkUserExists() {
const [user] = await getUserByUsername(username);
if (user?.userId) {
setUser(user);
} else {
history.push(ROUTES.NOT_FOUND);
}
}
.
.
특정 id의 페이지를 나타낼 시 유용할 듯!
참고
html {
scroll-behavior: smooth;
}
원하는 영역 선택하고 클릭하면 원하는 위치로 이동가능
스타일링 작성시 일반적 순서
:root { 글자색,애니메이션속도 등 자주 쓰일 값 변수로 저장}
*, body, global(h1~h5,p,url,img 등) 스타일링
buttons 스타일링,
그 다음 각각 컴포넌트들에 해당하는 부분으로 나눈다(or page를 나눈다)