[React/JS/ํŒ€ํ”„์ ] api๋กœ ๋ฐ›์€ ์ •๋ณด useState ๋ฐฐ์—ด์— ๋‹ด์•„ map ๋Œ๋ฆฌ๋ฉฐ ์ถœ๋ ฅํ•˜๊ธฐ

2024. 2. 27. 23:55ยท๐Ÿชด React
SMALL

 

ํ”„๋กœ์ ํŠธ ๋ฉ”์ธํŽ˜์ด์ง€์—์„œ

ํ•„์š”ํ•œ ์ •๋ณด๋“ค์„ api - axios get ํ•ด์„œ ๊ฐ€์ ธ์˜ค๊ณ ,

๊ทธ ์ •๋ณด๋“ค์„ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ๋ฌถ์–ด, ๋ฐฐ์—ด state (useState) ์— ํ•˜๋‚˜์”ฉ ๋‹ด๊ณ  map์œผ๋กœ ๋Œ๋ฆฌ๋ฉด์„œ ์ถœ๋ ฅํ–ˆ๋‹ค.

<Thumbnail>์€ ์ปดํฌ๋„ŒํŠธ ! 

 

๊ทธ ์™ธ์—๋„ input์— ๋ถ€์—ฌํ•˜๋Š” onKeyDown ์†์„ฑ ๋“ฑ์„ ๋ฐฐ์› ๋‹ค.

์ด๋ฒˆ์—๋Š” trim() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋„ ์ถ”๊ฐ€ !

 

 

 

(๋‹ค๋ฅธ ๋ถ„๋“ค์ด ์“ฐ์‹  ์ฝ”๋“œ๋„ ์žˆ์Œ /์Šคํƒ€์ผ์ปดํฌ๋„ŒํŠธ ๋ถ€๋ถ„์€ ์ƒ๋žต)

 

์œ ํŠœ๋ธŒ api ํ†ตํ•ด ๊ฐ€์ ธ์˜จ ์ •๋ณด -> ์ธ๊ธฐ๊ธ‰์ƒ์Šน์˜์ƒ๋“ค์˜ ์ฑ„๋„ ์ •๋ณด ~ ์œ ํŠœ๋ธŒ์ธ๋„ค์ผ์ด๋ฏธ์ง€(์ฃผ์†Œ), ์œ ํŠœ๋ธŒ์ฑ„๋„๋ช…, ์œ ํŠœ๋ธŒcustomUrl

 

 

import { useEffect, useState } from 'react';
import styled from 'styled-components';
import HeaderSlider from '../sliders/HeaderSlider';
import { useMostPopularVideos } from '../../hooks/useMostPopularChannel';
import { getMostChannelInfo, getMostPopularThumbnails } from '../../api/dataApi';
import Thumbnail from '../main/Thumbnail';
import { useNavigate } from 'react-router-dom';
import BodySlider from '../sliders/BodySlider';

export default function Main() {
  const navigate = useNavigate();

  const [channelInfos, setChannelInfos] = useState([]);
  const { data: videos, isLoading, isError } = useMostPopularVideos();
  const [searchTerm, setSearchTerm] = useState('');

  const searchIconSrc = 'https://s3.ap-northeast-2.amazonaws.com/cdn.wecode.co.kr/icon/search.png';
  const keyWords = ['๋จน๋ฐฉ', '์—ฌํ–‰', '์ƒํ™œ', '์šด๋™', '๋ทฐํ‹ฐ', 'ํŒจ์…˜'];
  const order = ['1st', '2nd', '3rd', '4th', '5th'];

  useEffect(() => {
    const getThumbnails = async () => {
      if (videos) {
        const newChannelInfos = [];
        for (const video of videos.slice(0, 5)) {
          const channelTitle = video.snippet.channelTitle;
          const channelThumbnail = (await getMostPopularThumbnails(video.snippet.channelId)).high.url;
          const channelCustomUrl = await getMostChannelInfo(video.snippet.channelId);
          const channelUrl = `https://www.youtube.com/${channelCustomUrl}`;
          const channelInfo = { channelTitle, channelThumbnail, channelUrl };
          // console.log(channelThumbnail);
          newChannelInfos.push(channelInfo);
        }
        setChannelInfos(newChannelInfos);
      }
    };
    getThumbnails();
  }, [videos]);

  const handleSearchInputChange = (e) => {
    setSearchTerm(e.target.value);
  };

  // Enter ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ๊ฒ€์ƒ‰ (=> ๋ฆฌ์ŠคํŠธํŽ˜์ด์ง€๋กœ ์ด๋™)
  const handleSearchEnter = (e) => {
    if (e.key === 'Enter') {
      if (!searchTerm.trim()) {
        return alert('๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
      }
      navigate(`/list/${searchTerm}`);
    }
  };

  // ๊ฒ€์ƒ‰์•„์ด์ฝ˜ ๋ˆŒ๋Ÿฌ ๊ฒ€์ƒ‰ (=> ๋ฆฌ์ŠคํŠธํŽ˜์ด์ง€๋กœ ์ด๋™)
  const handleSearchClick = () => {
    if (!searchTerm.trim()) {
      return alert('๊ฒ€์ƒ‰์–ด๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
    }
    navigate(`/list/${searchTerm}`);
  };

  const handleKeyWordClick = (keyword) => {
    navigate(`/list/${keyword}`);
  };

  const handleChannelClick = (channelUrl) => {
    window.open(channelUrl, '_blank');
  };

  if (isLoading) return <div>..Loading</div>;

  if (isError) return <div>{isError.message}</div>;

  return (
    <MainWrap>
      <HeaderSlider />
      <MainSearch>
        <SearchInputBox>
          <input
            id="search-input"
            type="search"
            placeholder="์›ํ•˜๋Š” ์ฃผ์ œ๋ฅผ ๊ฒ€์ƒ‰ํ•ด๋ณด์„ธ์š” !"
            value={searchTerm}
            onChange={handleSearchInputChange}
            onKeyDown={handleSearchEnter}
          />
          <img src={searchIconSrc} onClick={handleSearchClick} />
        </SearchInputBox>
        <SearchKeyWord>
          {keyWords.map((keyword) => {
            return (
              <KeywordText key={keyword} onClick={() => handleKeyWordClick(keyword)}>
                #{keyword}
              </KeywordText>
            );
          })}
        </SearchKeyWord>
      </MainSearch>
      <BodySlider />
      <MainBest>
        <MainBestTitle>
          <h3>Best YouTuber</h3>
          <p>์ด๋‹ฌ์˜ ์ธ๊ธฐ ์œ ํŠœ๋ฒ„</p>
        </MainBestTitle>
        <MainBestContWrap>
          {channelInfos.map((channelInfo, index) => (
            <BestYoutuber>
              <p>{order[index]}</p>
              <Thumbnail
                handleChannelClick={handleChannelClick}
                channelUrl={channelInfo.channelUrl}
                key={index}
                src={channelInfo.channelThumbnail}
                alt={`Thumbnail ${index + 1}`}
              />
              <p>{channelInfo.channelTitle}</p>
            </BestYoutuber>
          ))}
        </MainBestContWrap>
      </MainBest>
    </MainWrap>
  );
}
SMALL

'๐Ÿชด React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[React] ์—๋Ÿฌ : Element type is invalid: ... forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. (+์Šคํƒ€์ผ์ปดํฌ๋„ŒํŠธ ๋„ค์ด๋ฐ ์‹ค์ˆ˜)  (0) 2024.02.29
[React/JS/ํŒ€ํ”„์ ] ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ  (0) 2024.02.29
[React/ํŒ€ํ”„์ ] Youtube API ์‚ฌ์šฉํ•˜๊ธฐ  (0) 2024.02.26
[React] React-Query(aka. Tanstack-Query)  (0) 2024.02.23
[React] json-server์˜ ์ƒˆ๋กญ๊ฒŒ ๋ฐ”๋€ sort-order ๋ฐฉ์‹ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์š”์ฒญ ( - ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ)  (0) 2024.02.23
'๐Ÿชด React' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [React] ์—๋Ÿฌ : Element type is invalid: ... forgot to export your component from the file it's defined in, or you might have mixed up default and named imports. (+์Šคํƒ€์ผ์ปดํฌ๋„ŒํŠธ ๋„ค์ด๋ฐ ์‹ค์ˆ˜)
  • [React/JS/ํŒ€ํ”„์ ] ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ
  • [React/ํŒ€ํ”„์ ] Youtube API ์‚ฌ์šฉํ•˜๊ธฐ
  • [React] React-Query(aka. Tanstack-Query)
๋ จ๋””
๋ จ๋””
FE - ๊ฐœ๋ฐœ ๊ณต๋ถ€์˜ ๊ธฐ๋ก
  • ๋ จ๋””
    Ryeon's Devstory
    ๋ จ๋””
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (69)
      • ๐Ÿชด JavaScript & TypeScript (11)
        • ๐Ÿงฉ Algorithm (6)
      • ๐Ÿชด React (12)
      • ๐Ÿชด Next.js (8)
      • ๐Ÿชด HTML & CSS (2)
      • ๐Ÿชด DB & BaaS (5)
      • ๐Ÿชด Git &Github (0)
      • โœ๏ธ ํ”„๋กœ์ ํŠธ ๊ณผ์ • & ํšŒ๊ณ  (18)
      • โœ๏ธ Today what I Learned _T.. (11)
      • ๐Ÿ•Š๏ธ IT ์•„ํ‹ฐํด (0)
      • etc. (2)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

    • My Github ๐Ÿ˜บ
    • ๋„ค์ด๋ฒ„ ๋ธ”๋กœ๊ทธ ๐ŸŽต๐ŸŽง (์ผ์ƒ,์Œ์•… ๋“ฑ)
  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    BAAS
    useEffect
    ๋น„๋™๊ธฐํ†ต์‹ 
    ๋ Œ๋”๋งํŒจํ„ด
    ์˜ค๋ธ”์™„
    ์˜์กด์„ฑ๋ฐฐ์—ด
    ํ‹ฐ์Šคํ† ๋ฆฌ๋ธ”๋กœ๊ทธ์Šคํ‚จ
    tailwind
    useMutation
    State
    props
    ํ‹ฐ์Šคํ† ๋ฆฌ์ฑŒ๋ฆฐ์ง€
    axios
    json-server
    react
    NVM
    Next.js
    ๋ฆฌ์•กํŠธ์ฟผ๋ฆฌ
    ์›น๊ฐœ๋ฐœ ๊ธฐ์ดˆ
    ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ
    fetch
    ์—๋Ÿฌ
    route handler
    Array.filter
    reactquery
    Vite
    reacthook
    ์ปดํฌ๋„ŒํŠธ๋ถ„๋ฆฌ
    ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ
    Supabase
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.0
๋ จ๋””
[React/JS/ํŒ€ํ”„์ ] api๋กœ ๋ฐ›์€ ์ •๋ณด useState ๋ฐฐ์—ด์— ๋‹ด์•„ map ๋Œ๋ฆฌ๋ฉฐ ์ถœ๋ ฅํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”