어제 작성한 코드 정리
•
Card.js
import React from "react";
function Card({title, backgroundColor, children}) {
return (
<div style={{
margin: 10,
height: 50,
borderRadius: 5,
boxShadow: "0px 0px 5px gray",
backgroundColor: backgroundColor || "white"
}}>
{title && <h1>{title}</h1>}
{children}
</div>
)
}
export default Card;
JavaScript
복사
•
Comment.js
import React from "react";
function UserImg(props) {
return (
<img className="userImg"
src={props.user.imgUrl}
alt={props.user.userName}
/>
)
}
function UserInfo(props) {
return(
<div className = 'userInfo'>
<UserImg user={props.user}/>
<div className="userInfoName">
{props.user.userName}
</div>
</div>
)
}
function Comment(props) {
return (
<div className='comment'>
<UserInfo user={props.user} />
{/* 댓글 */}
<div className="comment">{props.content}</div>
{/* 작성 시간 */}
<div className="replydate">{props.replydate}</div>
</div>
)
}
export default Comment;
JavaScript
복사
•
Comment1. js
import React from "react";
function UserImg1(props) {
return (
<img className="userImg"
src={props.user.imgUrl}
alt={props.user.userName}
/>
)
}
function UserInfo1(props) {
return (
<div className="userinfo">
<UserImg1 user={props.user}/>
<div className="userinfoname">
{props.user.userName}</div>
</div>
)
}
function Comment1(props){
return (
<div className="comment">
<UserInfo1 user={props.user} />
{/* reply Content */}
<div className="commentContent"> {props.content} </div>
{/* reply replydate */}
<div className="replydate"> {props.replydate} </div>
</div>
)
}
export default Comment1
JavaScript
복사
•
Comment2.js
import React from "react";
const style = {
wrap: {
margin: 8,
padding: 8,
display: 'flex',
flexDirection: 'row',
border: '1px solid gray'
},
image: {
width: 50,
height: 50,
borderRadius: 25
},
contentContainer: {
marginLeft: 10,
display: 'flex',
flexDirection: 'column',
justifyContent : 'center'
},
commentText: {
color: 'black',
fontSize: 16,
marginBottom: 5
},
dataText: {
color: 'gray',
fontSize: 10
}
}
function Comment2(props){
return (
<div style={style.wrap}>
{/* 회원 정보 */}
<div>
<img src={props.user.imgUrl}
alt={props.user.userName}
style={style.image} />
<div style={{
color: 'blue',
fontSize: 12,
fontWeight: 'bold'
}}>{props.user.userName} </div>
</div>
<div style={style.contentContainer}>
{/* reply Content */}
<div style={style.commentText}>{props.content} </div>
{/* reply replydate */}
<div style={style.dataText}> {props.replydate} </div>
</div>
</div>
)
}
export default Comment2;
JavaScript
복사
•
CommentList.js
import React from "react";
import Comment2 from "./Comment2";
let comments = [
{
cno: 1,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '손흥민'
},
content: '1등이다!!!!!!',
replydate: '2024.06.20'
},
{
cno: 2,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '이강인'
},
content: '반갑습니다.',
replydate: '2024.06.21'
},
{
cno: 3,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '김민재'
},
content: '강인이 안녕.',
replydate: '2024.06.25'
}
]
function CommentList() {
return (
<div>
{
comments.map((comment) => {
return (
<Comment2 key={comment.cno} user={comment.user} content={comment.content} replydate={comment.replydate}></Comment2>
)
})
}
</div>
)
}
export default CommentList;
JavaScript
복사
•
ConfirmDialog.js
import React from "react";
import Comment2 from "./Comment2";
let comments = [
{
cno: 1,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '손흥민'
},
content: '1등이다!!!!!!',
replydate: '2024.06.20'
},
{
cno: 2,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '이강인'
},
content: '반갑습니다.',
replydate: '2024.06.21'
},
{
cno: 3,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '김민재'
},
content: '강인이 안녕.',
replydate: '2024.06.25'
}
]
function CommentList() {
return (
<div>
{
comments.map((comment) => {
return (
<Comment2 key={comment.cno} user={comment.user} content={comment.content} replydate={comment.replydate}></Comment2>
)
})
}
</div>
)
}
export default CommentList;
JavaScript
복사
•
Dialog.js
import React from "react";
import Comment2 from "./Comment2";
let comments = [
{
cno: 1,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '손흥민'
},
content: '1등이다!!!!!!',
replydate: '2024.06.20'
},
{
cno: 2,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '이강인'
},
content: '반갑습니다.',
replydate: '2024.06.21'
},
{
cno: 3,
user: {
imgUrl: 'https://encrypted-tbn1.gstatic.com/licensed-image?q=tbn:ANd9GcRpSo5X-9GknhLwsqbgdtHhueRB_ZitJC-6IPBvZ7KWWxgFHCl9oke9ouGB8DzTUth-fvavDDA1pSJBlc4',
userName: '김민재'
},
content: '강인이 안녕.',
replydate: '2024.06.25'
}
]
function CommentList() {
return (
<div>
{
comments.map((comment) => {
return (
<Comment2 key={comment.cno} user={comment.user} content={comment.content} replydate={comment.replydate}></Comment2>
)
})
}
</div>
)
}
export default CommentList;
JavaScript
복사
•
FancyBorder.js
import React from "react";
function FancyBorder(props) {
return (
<div className={'border-'+props.color}>
{props.children}
</div>
)
}
export default FancyBorder
JavaScript
복사
•
NumberList.js
import React from "react";
function NumberList(props) {
const NumberList = [11, 42, 78, 32, 90]
const listItem = NumberList.map((num, index) => <li key={index}>{num}</li>)
return (
<ul>
{listItem}
</ul>
)
}
export default NumberList;
JavaScript
복사
•
Player.js
import React from "react";
function NumberList(props) {
const NumberList = [11, 42, 78, 32, 90]
const listItem = NumberList.map((num, index) => <li key={index}>{num}</li>)
return (
<ul>
{listItem}
</ul>
)
}
export default NumberList;
JavaScript
복사
•
ProfileCard.js
import React from "react";
import Card from "./Card";
function ProfileCard(props) {
return (
<Card title="손흥민" backgroundColor="yellow" >
<p>안녕하세요. 축구선수 손흥민입니다.</p>
<p>저는 축구를 잘합니다.</p>
</Card>
)
}
export default ProfileCard
JavaScript
복사
•
Team.js
import React from "react";
import Player from "./Player";
// 배열 생성
let team = [
{name:'손흥민', no : '7'},
{name:'이강인', no : '19'},
{name:'김민재', no : '3'}
]
function Team(props) {
return (
<div>
{/* <Player playerName='손흥민1' playerNumber='71'></Player>
<Player playerName='손흥민2' playerNumber='72'></Player>
<Player playerName='손흥민3' playerNumber='73'></Player> */}
{
team.map((player) => {
return (
<Player playerName={player.name} playerNumber={player.no}></Player>
)
})
}
</div>
)
}
export default Team;
JavaScript
복사
•
WelcomeDialog.js
import React from "react";
import Dialog from "./Dialog";
function WelcomeDialog() {
return (
<Dialog title="환영합니다!!!!" message="회원 정보를 확인해주세요">
{/* <button>개인정보 수정</button> */}
</Dialog>
)
}
export default WelcomeDialog
JavaScript
복사
환경 설정
•
React Developer Tools 설치
Blog 제작 실습
•
Header - 블로그 이름, 설명, 객체로 받는다
import React from "react";
function Header({header}) {
// 블로그 이름, 설명, 객체로 받는다 {title: '', description: ''}
return (
<header>
<h1 className="title">{header.title}</h1>
<p>{header.description}</p>
</header>
)
}
export default Header
JavaScript
복사
•
Article
import React from "react";
function Article({title, date, imgUrl, content}) {
return (
<article>
<h2> {title} </h2>
<p> {date} </p>
<img src={imgUrl} />
<p> {content} </p>
</article>
)
}
export default Article
JavaScript
복사
•
Aside
import React from "react";
function Aside({recentPosts}) {
// 최근 글의 제목 링크
// [{name: '', linkUrl: ''}, {name: '', linkUrl: ''}, {name: '', linkUrl: ''}, ...]
return (
<aside>
<h4>카테고리</h4>
<ul>
{
recentPosts.map((item, index) => {
return (
<li key={index}>
<a href={item.linkUrl}>{item.name}</a>
</li>
)
})
}
</ul>
</aside>
)
}
export default Aside
JavaScript
복사
•
MainWrap
import React from "react";
import Article from "./Article";
import Aside from "./Aside";
function MainWrap({posts, recentPosts}) {
// let posts = [
// {title: 'aaa', date: '2024.06.10', imgUrl: 'git.png', content: '안녕하세요11'},
// {title: 'bbb', date: '2024.06.20', imgUrl: 'git.png', content: '안녕하세요12'},
// {title: 'ccc', date: '2024.06.30', imgUrl: 'git.png', content: '안녕하세요13'}
// ]
// let recentPosts = [
// { name: '111', linkUrl: '#' },
// { name: '222', linkUrl: '#' },
// { name: '333', linkUrl: '#' },
// { name: '333', linkUrl: '#' },
// { name: '333', linkUrl: '#' },
// { name: '333', linkUrl: '#' },
// { name: '333', linkUrl: '#' }
// ]
return (
<div id="wrap">
<section>
{
posts.map((post, index) => {
return (
<Article key={index}
title={post.title}
date={post.date}
imgUrl={post.imgUrl}
content={post.content}
></Article>
)
})
}
</section>
<Aside recentPosts={recentPosts}></Aside>
</div>
)
}
export default MainWrap
JavaScript
복사
•
Nav
import React from "react";
function Nav({nav}) {
// 메뉴 데이터 : 배열 [{name: '', linkUrl: ''}, {}, .....]
return (
<nav>
<ul>
{
nav.map((menu, index) => {
return (
<li key={index}>
<a href={menu.linkUrl}>{menu.name}</a>
</li>
)
})
}
</ul>
</nav>
)
}
export default Nav
JavaScript
복사
•
Footer
import React from "react";
function Footer({ copyright }) {
return (
<footer>
{copyright}
</footer>
)
}
export default Footer
JavaScript
복사
•
BlogMain
import React from "react";
import Header from "./Header";
import Nav from "./Nav";
import MainWrap from "./MainWrap";
import Footer from "./Footer";
// Headers
// Nav
// Main - Wrap => Article, Aside
// Footer
const blogData = {
header: {
title: 'React.js',
description: 'react blog'
},
nav: [
{name: 'html5', linkUrl: '#'},
{name: 'CSS', linkUrl: '#'},
{name: 'Javascript', linkUrl: '#'},
{name: 'jquery', linkUrl: '#' },
{name: 'react.js', linkUrl: '#'}
],
mainwrap: {
posts: [
{title: 'aaa', date: '2024.06.10', imgUrl: 'git.png', content: '안녕하세요11'},
{title: 'bbb', date: '2024.06.20', imgUrl: 'git.png', content: '안녕하세요12'},
{title: 'ccc', date: '2024.06.30', imgUrl: 'git.png', content: '안녕하세요13'}
],
recentPosts: [
{name: '111', linkUrl: '#'},
{name: '222', linkUrl: '#'},
{name: '333', linkUrl: '#'},
]
},
copyright: 'copyright by minsung'
}
function BlogMain() {
return (
<div>
<Header header={blogData.header}></Header>
<Nav nav={blogData.nav}></Nav>
<MainWrap posts={blogData.mainwrap.posts} recentPosts={blogData.mainwrap.recentPosts}></MainWrap>
<Footer copyright={blogData.copyright}></Footer>
</div>
)
}
export default BlogMain
JavaScript
복사
State
•
State란
◦
리액트에서 state는 컴포넌트의 상태를 의미
◦
리액트 컴포넌트의 변경 가능한 데이터를 state라고 함
◦
state는 사전에 미리 정해진 것이 아니라 리액트 컴포넌트를 개발하는 각 개발자가 직접
정의해서 사용
▪
state 정의할 때 꼭 렌더링이나 데이터 흐름에 사용되는 값만 state 에 포함시켜야 함
▪
state가 변경될 경우 컴포넌트가 재렌더링 되기에 렌더링과 데이터 흐름에 관련 없는 값을 포함하면
컴포넌트가 다시 렌더링되어 성능 저하가 발생할 수 있음
훅(hook)
•
함수형 컴포넌트에 이런 기능을 지원하기 위해서 나온 것이 바로 훅
•
훅을 사용하면 함수 컴포넌트도 클래스 컴포넌트의 기능을 모두 동일하게 구현할 수 있음
(!한번 렌더링되면 그 값은 변하지 않음)
•
Hook - 자주 사용되는 훅의 종류
◦
useState()
◦
userEffect()
◦
userMemo() ⇒ 버전을 많이 탐
◦
useCallback()
◦
useRef()
useState() 실습
•
MyButton.js
import React from "react";
function MyButton(props) {
const handlerDelete = (id, event) => {
alert('안녕하세요')
console.log(id, event.target)
}
return (
<button onClick={(event) => handlerDelete(1, event)}>
삭제하기
</button>
)
}
export default MyButton
JavaScript
복사
•
const [변수명, set 함수명] = useState()
◦
setCount() ⇒ state 값을 변경해주는 함수, 실행 ⇒ 재 렌더링
import React from "react";
import {useState} from "react";
// let count = 0
// const countHandle = () => {
// // count++
// setCount(count + 1)
// console.log(count)
// }
function Counter() {
// const [변수명, set함수명] = useState()
const [count, setCount] = useState(0)
// count = 0
// setCount() => state 값을 변경해주는 함수, 실행 => 재 렌더링
return (
<div>
<p>총 {count}번 클릭!</p>
<button onClick={() => setCount(count + 1)}>
Click
</button>
</div>
)
}
export default Counter
JavaScript
복사
•
ConfirmButton.js
import React from "react";
import { useState } from "react";
function ConfirmButton(props) {
const [isConfirmed, setIsConfirmed] = useState(false)
const handleConfirm = () => {
setIsConfirmed( (preIsConfirmed) => !preIsConfirmed)
}
return (
<button onClick={handleConfirm} disabled={isConfirmed}>
{isConfirmed ? '확인 완료' : '확인하기'}
</button>
)
}
export default ConfirmButton
JavaScript
복사
•
isLogin Toolbar ⇒ 로그인 상태 전달, 로그인 핸들러, 로그아웃 핸들러
// isLogin Toolbar => 로그인 상태 전달, 로그인 핸들러, 로그아웃 핸들러
import React from "react";
import { useState } from "react";
import Toolbar from "./ToolBar";
function MainPage(props) {
// 변경값 : 로그인 여부
const [isLogin, setIsLogin] = useState(false)
const onClickLogin = () => {
setIsLogin(true)
}
const onClickLogout = () => {
setIsLogin(false)
}
return (
<>
<Toolbar
isLogin={isLogin}
onClickLoginHandle={onClickLogin}
onClickLogoutHandle={onClickLogout}
></Toolbar>
안녕하세요. 메인페이지 입니다.
</>
)
}
export default MainPage
JavaScript
복사
•
NameForm.js
import React from "react";
import { useState } from "react";
function NameForm(props) {
const [value, setValue] = useState('')
const handleChange = (event) => {
setValue(event.target.value)
console.log(event.target.value)
}
const handleSubmit = (event) => {
event.preventDefault()
alert('입력한 이름 : ' + value)
}
return (
<form onSubmit={handleSubmit}>
<label>이름</label>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">제출</button>
</form>
)
}
export default NameForm
JavaScript
복사
•
SignUp.js
import React, { useState } from "react";
function SignUp(props) {
const [userName, setUserName] = useState('')
const [gender, setGender] = useState('M')
return (
<form onSubmit={(event) => {
event.preventDefault()
alert(`이름 : ${userName}, 성별 : ${gender}`)
}}>
<label>이름</label>
<input
type="text"
value={userName}
onChange={(event) => {
setUserName(event.target.value)
}} />
<br />
<label>성별</label>
<select
value={gender}
onChange={(event) => {
setGender(event.target.value)
}}
>
<option value="M">남성</option>
<option value="F">여성</option>
</select>
<br/>
<button type="submit">제출</button>
</form>
)
}
export default SignUp
JavaScript
복사
•
Dialog.js
import React from "react";
import FancyBorder from "./FancyBorder";
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1>{props.title}</h1>
<p>{props.message}</p>
{props.children}
</FancyBorder>
)
}
export default Dialog
JavaScript
복사
•
Reservation.js (예약)
import React from "react"
import { useState } from "react"
// 방문객수 text, 조식여부 checkbox, 요청사항 textarea
function Reservation(props) {
const [guestOfNum, setGuestOfNum] = useState(1)
const [haveBreakfast, setHaveBreakfast] = useState(false)
const [msg, setMsg] = useState('요청사항을 입력하세요!')
const handleSubmit = (event) => {
event.preventDefault()
alert(`방문객 수 : ${guestOfNum}명 \n조식 여부 : ${haveBreakfast} \n요청사항 : \n ${msg}`)
}
return (
<form onSubmit={handleSubmit}>
<label>방문객 수</label>
<input type="text" value={guestOfNum}
onChange={(event) => {
setGuestOfNum(event.target.value)
}} />
<br/>
<label>조식 여부</label>
<input type="checkbox" checked={haveBreakfast}
onChange={(event) => {
setHaveBreakfast(event.target.checked)
}} />
<br/>
<label>요청사항</label>
<br/>
<textarea value={msg}
onChange={(event) => {
setMsg(event.target.value)
}} ></textarea>
<br/>
<button type="submit">제출</button>
</form>
)
}
export default Reservation
JavaScript
복사
•
SignUpDialog.js
import React from "react";
import { useState } from "react";
import Dialog from "./Dialog";
let room = []
function SignUpDialog(props) {
const [userName, setUserName] = useState('');
const handleClick = (event) => {
// 배열에 추가
room.push(userName)
setUserName('')
alert(`환영합니다!! ${userName}님 `)
}
return (
<Dialog title="스터디 팀 참여" message="이름을 입력하세요">
<input type="text" value={userName}
onChange={(event) => {
setUserName(event.target.value)
}}
/>
<button onClick={handleClick}>참여하기</button>
<div>
{
room.map((name, index) => {
return <p key={index}>{name}</p>
})
}
</div>
</Dialog>
)
}
export default SignUpDialog
JavaScript
복사