[React]외부 API 연동하여 뉴스 뷰어 만들어보기-2

UkiUkhui·2021년 10월 24일
0

React 공부중

목록 보기
19/25

1. 데이터 연동하기

import axios from "axios";
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import NewsItems from "./NewsItems";

const NewsListBlock = styled.div`
  box-sizing: border-box;
  padding-bottom: 3rem;
  width: 768px;
  margin: 0 auto;
  margin-top: 2rem;
  @media screen and (max-width: 768px) {
    width: 100%;
    padding-left: 1rem;
    padding-right: 1rem;
  }
`;

const NewsList = () => {
    const [articles, setArticles] = useState(null);
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        const fetchData = async () => {
            setLoading(true);
            try {
                const res = await axios.get('https://newsapi.org/v2/top-headlines?country=kr&apiKey=cecec5a2918645be8f6bddfc01c8f07d');
                setArticles(res.data.articles);
            } catch (e) {
                console.log(e);
            }
            setLoading(false);
        }
        fetchData();
    }, []);

    if (loading) {
        return <NewsListBlock>로딩중...</NewsListBlock>
    }
    if (!articles) {
        return null;
    }
    return (
        <NewsListBlock>
            {articles.map(article => <NewsItems key={article.url} article={article} />)}
        </NewsListBlock>
    );
};

export default NewsList;
  • map 함수 사용 전, articles의 값이 null인지 반드시 확인하지 않으면 에러발생
  • useEffect에서 []를 통해 newsAPI는 한 번만 불러오게 함.
  • useEffect의 콜백함수에 async를 붙이면 리턴되는 뒷정리함수에 걸리기 때문에 사용하는 내부함수에서 걸어줘야 함.
  • axios get을 통해 받아온 res.data에 articles로 뉴스 정보들을 받아옴.

2. 카테고리 구현

const categories = [
    {
        name: 'all',
        text: '전체보기',
    },
    {
        name: 'business',
        text: '비즈니스',
    },
    {
        name: 'entertainment',
        text: '엔터테인먼트',
    },
    {
        name: 'health',
        text: '건강',
    },
    {
        name: 'scinece',
        text: '과학',
    },
    {
        name: 'sports',
        text: '스포츠',
    },
    {
        name: 'technology',
        text: '기술',
    },
];

const Categories = () => {
    return (
        <CategoriesBlock>
            {categories.map(c => <Category key={c.name}>{c.text}</Category>)}
        </CategoriesBlock>
    )
}

export default Categories;
  • name : 실제 카테고리 값
  • text : 렌더링할 때 사용되는 텍스트 값

App.js

import axios from "axios";
import React, { useCallback, useState } from "react";
import Categories from "./component/Categories";
import NewsList from "./component/NewsList";

const App = () => {
  const [category, setCategory] = useState('all');
  const onSelect = useCallback(category => setCategory(category), []);

  return <>
    <Categories category={category} onSelect={onSelect} />
    <NewsList category={category} />
  </>
}

export default App;
  • useCallback을 통해 특정함수를 재사용함
  • []을 통해 변경되지 않는 한 계속 재사용하겠다는 뜻.
  • onSelect의 경우 category를 인자로 받아 내용을 변경

Categories.js

const Categories = ({ category, onSelect }) => {
    return (
        <CategoriesBlock>
            {categories.map(c =>
                <Category
                    key={c.name}
                    active={category === c.name}
                    onClick={() => onSelect(c.name)}>
                    {c.text}
                </Category>)}
        </CategoriesBlock>
    )
}
  • onClick 이벤트로 onSelect를 발생시킴.

2.1. API 호출 시 카테고리 지정

import axios from "axios";
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import NewsItems from "./NewsItems";

const NewsListBlock = styled.div`
  box-sizing: border-box;
  padding-bottom: 3rem;
  width: 768px;
  margin: 0 auto;
  margin-top: 2rem;
  @media screen and (max-width: 768px) {
    width: 100%;
    padding-left: 1rem;
    padding-right: 1rem;
  }
`;

const NewsList = ({ category }) => {
    const [articles, setArticles] = useState(null);
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        const fetchData = async () => {
            setLoading(true);
            try {
                const query = category === 'all' ? '' : `&category=${category}`;
                const res = await axios.get(`https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=cecec5a2918645be8f6bddfc01c8f07d`);

                setArticles(res.data.articles);
            } catch (e) {
                console.log(e);
            }
            setLoading(false);
        }
        fetchData();
    }, [category]);

    if (loading) {
        return <NewsListBlock>로딩중...</NewsListBlock>
    }
    if (!articles) {
        return null;
    }
    return (
        <NewsListBlock>
            {articles.map(article => <NewsItems key={article.url} article={article} />)}
        </NewsListBlock>
    );
};

export default NewsList;
  • 카테고리가 all이면 공백문자열을, all이 아니라면 "&category=${category}" 형태의 문자열을 만들도록 함
  • https://newsapi.org/v2/top-headlines?country=kr&category=entertainment&... 이런 형태로 만들어지기 때문에 중간에 query값을 넣어서 동적으로 변하도록 설정함.
  • useEffect 두번째 인자에는 category가 변경될 때마다 불러와야하기 때문에 [category] 값을 넣어줌

3. 라우터 이용하기

App.js

import axios from "axios";
import React, { useCallback, useState } from "react";
import { Route } from "react-router";
import NewsPage from "./component/NewsPage";


const App = () => {

  return <>
    <Route path="/:category?" component={NewsPage} />
  </>
}

export default App;
  • ?의 의미는 category값이 선택적이라는 것으로, 있을 수도 없을 수도 있는 값임.

NewsPage.js

import React from "react";
import Categories from "./Categories";
import NewsList from "./NewsList";

const NewsPage = ({ match }) => {
    const category = match.params.category || 'all';

    return (
        <>
            <Categories />
            <NewsList category={category} />
        </>
    )
}

export default NewsPage;
  • match.params.category || 'all' : 카테고리 값이 정해졌으면 그 값을 쓰고, 정해지지 않았다면 all을 사용한다.

Categories.js


const Categories = ({ category, onSelect }) => {
    return (
        <CategoriesBlock>
            {categories.map(c =>
                <Category
                    key={c.name}
                    active={category === c.name}
                    exact={'all' === c.name}
                    to={c.name === 'all' ? '/' : `/${c.name}`}>
                    {c.text}
                </Category>)}
        </CategoriesBlock>
    )
profile
hello world!

0개의 댓글