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을 만들어서 사용하지 않아도 되지만, 조금 더 간결한 코드를 만들수도 있으니
참고할 것 !