Section 1에서 구현한 나만의 아고라 스테이츠를 React로 다시 개발하였습니다.
create-react-app으로 프로젝트 생성
기존에 만든 나만의 아고라 스테이츠를 React로 옮기기
로컬 환경에서 실행한 나만의 아고라 스테이츠 서버에서 discussions 데이터를 조회합니다.
create-react-app
를 다운 받은 후 필요없는 파일은 지운다.- logo.svg
- reportWebVitals.js
- setupTests.js
- App.test.js
App.js
import './App.css';
function App() {
return (
<div>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Form.js
로 만들어본다.Discussions.js
Discussion.js
→ 질문들을 map
을 이용하여 Discussion.js
에 뿌려준다.
→ 페이지가 단순하기 때문에 굳이 SPA 없이 (따라서 라우터도 필요없다.) 컴포넌트 파일을 만들어서 바로 만든다.
Form.js
Discussion.js
Discussions.js
Form.js
전에 html로 만든 form 코드를 복사한다.
가져온 후에 jsx로 변환한다.
크롬에서 Copy element를 통해 form__container를 다 가져온다.
복사한 form 코드를 App.js
에서 사용하기 위해 export를 해주고 그 안에 return을 해준다.
에러나 나온다면 화살표가 해당하는 곳에서 확인할 수 있다. JSX 요소 'input'에 닫는 태그가 없는 것으로 알 수 있다.
class를 className으로 바꿔줘야 한다. 같은 class이기에 cmd + D 또는 cmd + shift + L을 눌러주면 한번에 선택이 가능하다.
JSX는 for가 아니라 htmlFor를 적어야 한다. 그래서 <label for="name">Enter your name: </label>
이렇게 된 것을 <label htmlFor="name">Enter your name: </label>
이렇게 적는다.
App.js
App.js
에서 form을 받아오면 된다.
import를 해주고 div안에 form을 써준다.
Discussions도 연결해준다.
usestate를 통해 데이터가 올 때마다 바꿔준다.
이제 useEffect를 통해 데이터를 받아온다.
[]
)을 표시해줘야 데이터를 한번만 받아오게 된다. 안그러면 데이터를 계속 받아오게 된다. (dependency array)import './App.css';
import { useEffect, useState } from 'react';
import { Discussions } from './components/Discussions';
import {Form} from "./components/Form"
function App() {
const [discussions, setDiscussions] = useState([]);
useEffect(() => {
fetch('http://localhost:4000/discussions')
.then(res => res.json())
.then(data => {
setDiscussions(data);
})
}, []);
return (
<div>
<Form />
<Discussions discussions={discussions}></Discussions>
</div>
);
}
export default App;
Discussions.js
form.js와 같이 복사를 하는데 이번에는 <section class="discussion__wrapper"></section>
에서 복사를 한다.
에러를 확인해보니 이미지에 닫는 태그가 없어서 닫는 태그를 달아준다.
필요없는 li
는 없애주고 ul
로 닫아준다.
App.js
와 연결해주기 위해 export를 해준다. 그리고 return도 같이 해준다.
class를 claaName으로 해준다.
map을 사용해서 질문질문 하나를 컴포넌트로 만들어준다.
export const Discussions = () => {
return(
<section class="discussion__wrapper">
<ul class="discussions__container">
<li class="discussion__container">
<div class="discussion__avatar--wrapper">
<img class="discussion__avatar--image" src="https://avatars.githubusercontent.com/u/12145019?s=64&u=5c97f25ee02d87898457e23c0e61b884241838e3&v=4" alt="avatar of kimploo" />
</div>
<div class="discussion__content">
<h2 class="discussion__title">
<a href="https://github.com/codestates-seb/agora-states-fe/discussions/6">[notice] 좋은 질문하는 법</a>
</h2>
<div class="discussion__information">
kimploo / 2022-04-22T14:08:33Z
</div>
</div>
<div class="discussion__answered"><p>☑</p></div>
</li>
</ul>
</section>
)}
현재 <li>
태그로 묶인 곳이 반복되고 있기 때문에 map을 이용해서 데이터로 뿌려줄 것이다. 따라서 <li>
태그는 Discussion.js로 옮겨서 따로 컴포넌트를 만들어주고 Discussions.js에서는 <li>
를 없애준다. 없애면 바로 아래와 같은 코드가 된다.
현재 Discussions가 빈 껍데기이기에 App.js에서 작업을 해줘야 한다.
App.js에서 작업이 완료가 되었으면 이제 map으로 처리를 해주면 된다.
ul
태그 안에 중괄호를 먼저 쓰고 {discussions.map}
을 해서 discussion의 리턴 <Discussion></Discussion>
을 해주면 된다.
그리고 Discussion에 discussion을 내려줘야 한다.
그리고 map에서 key를 써줘야 한다.
그리고 Discussion의 import를 잊지 않는다.
{discussions.map(discussion= > {return <Discussion discussion={discussion} key={discussion.id}></Discussion>})}
import { Discussion } from './Discussion';
export const Discussions = (discussions) => {
return(
<section className="discussion__wrapper">
<ul className="discussions__container">
{discussions.map(discussion => {
return <Discussion discussion={discussion} key={discussion.id}></Discussion>
})}
</ul>
</section>
);
}
index.css
index.html
index.html
기본값이 div로 되어 있는데 이거를 css에는 main으로 되어있기에 main으로 바꿔준다.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<main id="root"></main>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Discussion.js
Discussion.js에서는 <li>
태그를 따로 컴포넌트로 만든다.
먼저 export 해준다.
데이터를 받아온다.
Discussion은 데이터가 올 때마다 데이터가 렌더링이 되면서 바뀌기 때문에 App.js에서
usestate를 사용해야 한다.
App.js
에서 처리한 데이터를 Discussion.js로 받아온다.
export const Discussion = () => {
<li class="discussion__container">
<div class="discussion__avatar--wrapper">
<img class="discussion__avatar--image" src="https://avatars.githubusercontent.com/u/12145019?s=64&u=5c97f25ee02d87898457e23c0e61b884241838e3&v=4" alt="avatar of kimploo" />
</div>
<div class="discussion__content">
<h2 class="discussion__title">
<a href="https://github.com/codestates-seb/agora-states-fe/discussions/6">[notice] 좋은 질문하는 법</a>
</h2>
<div class="discussion__information">
kimploo / 2022-04-22T14:08:33Z
</div>
</div>
<div class="discussion__answered"><p>☑</p></div>
</li>
}
export const Discussion = ({discussion}) => {
const {url, author, avatarUrl, title, createdAt, answer} = discussion;
return (
<li className="discussion__container">
<div className="discussion__avatar--wrapper">
<img className="discussion__avatar--image" src={avatarUrl} alt={`avatar of ${author}`} />
</div>
<div className="discussion__content">
<h2 className="discussion__title"><a href={url}>{title}</a></h2>
<div className="discussion__information">{`${author} / ${new Date(createdAt).toLocaleString()}`}</div>
</div>
<div className="discussion__answered">
<p className="discussion__isAnswered">{answer ? "✅" : "❎" }</p>
</div>
</li>
)}