React

[React] axios 응용 2

썽연 2021. 10. 16. 16:06
728x90

axios를 이용하여 news api를 가져와 뉴스를 보였고, 

styled-component를 이용하여 보기 좋게 뉴스 뷰어를 만들었다.

 

위에 카테고리별로 분류를 해주면 더 좋을 것 같다.

 

최종 결과물은 다음과 같다.

나도 css잘하고싶다..........

css공부나 더 해야겠따.

 

pages/NewsPage.js

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

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

  return(
    <>
    <Categories/>
    <NewsList category={category}/>
    </>
  )
}
export default NewsPage;

현재 선택된 카테고리 값을 URL파라미터를 통해 사용할 것이므로

카테고리 커뫂넌트에서 현재 선택된 카테고리 값을 알려줄 필요가 없어, onSelect 함수 따로 전달x

 

 

App.js

import React from 'react';
import { Route } from 'react-router-dom';
import NewsPage from './pages/NewsPage';

const App = () => {
  return (
    <Route path="/:category?" component={NewsPage}/>
  );
};

export default App;

path에서 사용된 /:category? 와 같은 형태로 뒤에 물음표가 붙는다면, category값이 선택적이라는 의미이다.

category URL 파라미터가 없다면 전체 카테고리를 선택한것으로 간주한다.

 

 

categories.js

import React from 'react';
import styled from 'styled-components';
import { NavLink } from 'react-router-dom';

const categories = [
  {
    name: 'all',
    text: '전체보기',
  },
  {
    name: 'business',
    text: '비즈니스',
  },
  {
    name: 'entertainment',
    text: '엔터테인먼트',
  },
  {
    name: 'health',
    text: '건강',
  },
  {
    name: 'science',
    text: '과학',
  },
  {
    name: 'sports',
    text: '스포츠',
  },
  {
    name: 'technology',
    text: '기술',
  },
];
const CategoriesBlock = styled.div`
  display: flex;
  padding: 1rem;
  width: 768px;
  margin: 0 auto;
  @media screen and (max-width: 768px) {
    width: 100%;
    overflow-x: auto;
  }
`;

const Category = styled(NavLink)`
  font-size: 1.125rem;
  cursor: pointer;
  white-space: pre;
  text-decoration: none;
  color: inherit;
  padding-bottom: 0.25rem;

  &:hover {
    color: $495057;
  }
  &.active{
      font-weight: 600;
      border-bottom: 2px solid #22b8cf;
      color: #22b8cf;
      &:hover {
        color: #3bc9db;
      }
    }
  &+& {
    margin-left: 1rem;
  }
`;
const Categories = () => {
  return (
    <CategoriesBlock>
      {categories.map((c) => (
        <Category
          key={c.name}
          activeClassName="active"
          exact={c.name==='all'}
          to={c.name==='all'?'/':`/${c.name}`}
        >
          {c.text}
        </Category>
      ))}
    </CategoriesBlock>
  );
};
export default Categories;

네브바 형태로, 카테고리를 주기 위해

카테고리 목록을 만들어주었고,

스타일들을 주었다.

 

navLink로 만들어진 카테고리 컴포넌트에 to 값은

/카테고리이름으로 설정해주고,

전체보기는 예외적으로 all이 아닌 / 으로 설정했다.

exact = {c.name === 'all'}일땐 true를 설정해주어야한다.

설정하지 않을시 다른 카테고리가 선택되었을 때 전체보기 링크에 active스타일이 적용된다.

 

 

usePromis같은 함수는 custom hook으로 만들 수 있다.

프로젝트의 다양한 곳에서 사용될 수 있는 유틸함수들은 lib디렉터리 안에 생성한다.

 

lib/usePromise.js

import {useState, useEffect} from 'react';

export default function usePromise(promiseCreator, deps){
  const [loading, setLoading] = useState(false);
  const [resolved, setResolved] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() =>{
    const process = async()=>{
      setLoading(true);
      try{
        const resolved = await promiseCreator();
        setResolved(resolved);
      }
      catch(e){
        setError(e);
      }
      setLoading(false);
    };
    process();
  // eslint-disable-next-line react-hooks/exhaustive-deps
  },deps);

  return [loading,resolved, error]
}

 

components/NewsList.js

import React from 'react';
import styled from 'styled-components';
import NewsItem from './Newsitem';
import axios from 'axios';
import usePromise from '../lib/usePromise';

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 [loading, response, error] = usePromise(() => {
    const query = category === 'all' ? '' : `&category=${category}`;
    return axios.get(
      `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=api`,
    );
  }, [category]);

  if (loading) {
    return <NewsListBlock>대기 중 ...</NewsListBlock>;
  }
  if (!response) {
    return null;
  }
  if (error) {
    return <NewsListBlock>에러발생!</NewsListBlock>;
  }
  const { articles } = response.data;
  return (
    <NewsListBlock>
      {articles.map((article) => (
        <NewsItem key={article.url} article={article} />
      ))}
    </NewsListBlock>
  );
};

export default NewsList;

usePromise를 사용하면 NewsList에서 대기 중 상태 관리와 useEffect설정을 직접 하지 않아도 되므로 코드가 훨씬 간결하다!

요청 상태를 관리할 때 무조건 custom Hook을 만들어서 사용하지 않아도 되지만, 조금 더 간결한 코드를 만들수도 있으니

참고할 것 ! 

 

728x90