folder2 생성, LinkedButton.js생성, Notification.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App2';
import reportWebVitals from './reportWebVitals';
import App2 from "./App2";
import App3 from "./App3";
import App33 from "./App33";
import App5 from "./App5";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// strict mode : 자바스크립트의 문법을 강력하게 확인한다는 의미. 자바스크립트는 간소화된 문법이다. 그렇지만 이게 있으면 좀 더 강하게 확인한다
// React.StrictMode : 배포 버전에는 제외되고 개발 버전에서는 동작하는 엄격모드, 몇 가지 함수를 중복 실행 하여 잘못된 것이 없는지 개발자에게
// 확인하도록 한다. 다 주석 처리해도 잘 실행된다
// <React.StrictMode>
//{/*/!* strict 모드란 react 앱 내의 잠재적인 문제를 알아내기 위한 도구라고 나와있습니다.*!/,*/}
//{/*/!*<App />*!/*/}
//{/*/!* <App2/>*!/*/}
//{/*/!* <App3/>*!/*/}
//{/*/!* <App33/>*!/*/}
//{/* */}
//{/*/!*</React.StrictMode>*!/*/}
<App5/> <---!!!! 이거 사용한다
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
// App5.jsx
import React from "react";
import NotificationList from "./folder2/NotificationList";
import CountButton from "./folder2/CountButton";
function App5(){
return (
<div className = {"container"}>
<NotificationList/>
<CountButton/>
</div>
)
}
export default App5;
//Notification.jsx
import React from "react";
const styles = {
wrapper : {
margin : 8,
padding : 8,
display : "flex",
flexDirection : "row",
border : "1px solid grey",
borderRadius : 16,
},
messageText :{
color : "black",
fontSize : 16,
}
}
class Notification extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return(
<div style = {styles.wrapper}>
<span style = {styles.messageText}>
{this.props.message}
</span>
</div>
);
}
}
export default Notification;
// NotificationList.jsx
import React from "react";
import Notification from "./Notification";
// 서버에서 전송되어야 할 데이터, 서버가 없기 때문에 대체. 통신모듈이 없기 때문에 임의로 만들어준 데이터
const msgData = [
{id : 1, message : "안녕하세요. 오늘 일정입니다"},
{id : 2, message : "점심 식사 시간입니다."},
{id : 3, message : "이제 곧 미팅이 시작됩니다"},
];
// 자바 스크립트 타이머. 객체 정보를 저장할 변수
let timer;
// 클래스 컴포넌트를 사용
class NotificationList extends React.Component {
// 클래스 컴포넌트의 생성자 부분
constructor(props) {
super(props);
// state 객체 선언 (멤버변수로 선언)
// 클래스(객체)의 멤버 변수로 선언
this.state = {
// 메시지를 저장할 배열
notification : [],
};
}
// 화면을 생성(랜더링)하는 함수
render() {
return (
<div>
{/*map : es6버전에서 추가된 배열 관련 함수. 지정한 배열의 크기만큼 반복하고 결과를 배열로 반환하는 함수*/}
{this.state.notification.map((item)=>{
return <Notification key={item.id} message = {item.message}/>
})}
</div>
);
}
//생명주기 할려고 일부러 class로 만들었다고 함.
// 컴포넌트가 마운드된 후 실행되는 생명주기 함수
// 다 동작하고 난 다음(constructer -> render -> componentDidMount) 동작됨
componentDidMount() {
// object 타입을 사용한 확장 표현식
const {notification } = this.state; //현재 둘 다 비어있다. state가 비어있으니까(notification : [])
// 타이머를 사용하여 지정된 시간에 한 번 씩 반복함
timer = setInterval(()=>{ //setInterval : 지정된 시간 동안 계속 반복.반복실행
// 지역변수 notification 크기가 전역 변수인 배열 msgData의 크기보다 작을 경우 if문 실행됨
// 자바스크립트는 one thread 방식, 비동기실행방식이다. 이벤트 큐에 넣어주고 자기는 떠나며 쿨타임 찰 때 까지 반복함
if (notification.length < msgData.length) { //현재 notification.length는 0. => 방금 notification의 크기가 1이 됨 // 나중에 3이 되면 3과 3은 동일하니까 아래로 간다
const index = notification.length; //현재는 0이 들어감 -> 현재 1이다
notification.push(msgData[index]); //push로 가장 뒤에 msg의 데이터를 넣어준다
// setState() 함수를 사용하여 state 객체를 수정함
this.setState({ //state 수정. notification에 notification을 넣는다
notification : notification, //전역변수 notification : 여기서 선언한 지역변수 notification. // 넣어주기 때문에 1이 된다. // 2가 된다
});
}
else {
clearInterval(timer); // false가 되면 이곳으로 와서 timer를 삭제한다 -> 반복 끝남.
}
}, 1000); // 1초 후에 다시 실행됨
}
}
export default NotificationList;
//LinkButton.js
import React from "react";
// state : 현재 컴포넌트의 상태를 나타내는 객체, 현재 컴포넌트에서 변경가능한 객체, state는 직접적인 수정이 불가능함(화면 렌더링과 관련이 있음)
// setState() : state의 값을 변경하는 함수
// react의 생명주기 : 컴포넌트가 생성되고 내용이 변경되고 컴포넌트가 삭제되는 상태
// componentDidMount : 컴포넌트가 생성되고 화면에 렌더링 된 이후 실행되는 함수
// componentDidUpdate : 컴포넌트의 상태가 변경된 후 실행되는 함수, props 변경, setState() 함수 실행
// forceUpdate()를 통한 강제 업데이트 후 동작함.
// componentWillUnmount : 부모 컴포넌트에서 더이상 해당 컴포넌트를 사용하지 않아 삭제된 후 실행
class LinkButton extends React.Component {
constructor(props) {
super(props);
//state 설정
this.state = {
linked : false,
}
//현재 state 값 수정
this.setState({
linked : true, //이런 식으로 변경해준다
})
}
}
// CountButton.jsx
import React from "react";
import Button from "react-bootstrap/Button";
class CountButton extends React.Component {
constructor(props) {
super(props);
// state 객체 생성
//setState()를 사용해야만 여기서 선언한 객체를 수정할 수 있다.
this.state = {
count: 0,
}
// 클래스의 멤버 변수(객체의 멤버 변수) count 선언 count: 0 가져옴
this.count = this.state.count;
}
//메서드 2개 생성
countUp = () => {
// this.state.count = this.state.count + 1
console.log(" 이전 this.count : " + this.count);
this.count = this.count + 1;
// setState() 함수를 실행해야만 state의 값이 수정이 되고, state의 값이 수정되면 render() 함수가 재실행됨
this.setState({
count : this.count
}
)
console.log(" + 사용후 this.count : " + this.count);
};
countDown = () => {
// this.state.count = this.state.count - 1
console.log(" 이전 this.count : " + this.count);
this.count = this.count - 1;
// 화면의 count 숫자 바뀜
this.setState({
count : this.count
})
console.log("- 사용 후 this.count : " + this.count);
};
render() {
return(
<div>
<label className={"form-label"}> count : <span>{this.count}</span></label><br/>
<Button variant = {"primary"} onClick = {this.countUp}>+</Button>
<Button variant = {"danger"} onClick = {this.countDown}>-</Button>
</div>
)
}
}
export default CountButton;
setState()를 사용해야만 state 값을 변경할 수 있다 => 버튼 눌러서 count 값 바꿀려면 setState()를 사용해야만 했다.
// CountButton2.jsx
// hooks 예제
// useState() 훅을 사용하기 위해서 미리 import 해야 한다
import React, {useState, useEffect} from "react";
// hooks : 기존에 존재하는 기능을 추가로 함께 사용하도록 하는 것.
// state와 react 생명주기 기능을 원하는 시점에 실행할 수 있도록 하는 기능
// 우리는 useState useEffect 두 개 배울 예정
// useState : state 객체를 함수 컴포넌트에서 사용할 수 있도록 하는 기능.
// useEffect : useState()와 함께 가장 많이 사용되는 hooks. react 생명주기 함수인 componenDidMount,
// componentDidUpdate ,componentWillUnmount의 기능을 하나로 합한 기능
// useMemo : 재렌더링 시 연산량이 높은 작업을 반복하는 것을 피할 수 있게 하는 기능
// useCallback : useMemo와 비슷한 기능, 값이 아닌 함수를 반환함.
// useRef : 지정한 컴포넌트에 접근 할 수 있는 객체
// 훅을 사용 시 미리 해당 함수를 import해서 사용해야 함
// useState : class 컴포넌트에 존재하는 state 객체를 함수 컴포넌트에서 사용할 수 있도록 함.
// 사용법 :
// const [변수명, set함수명] = useState(초기값);
// 변수명 : state로 지정할 지역변수명
// set함수명 : state로 지정된 변수명에 접두사 set을 붙여서 사용, 카멜명명법 사용해야 한다.
// useState(초기값) : 확장표현식을 통해서 지정한 변수를 state 객체로 설정함, 초기값은 state로 설정된 변수의
// 초기값을 말한다.
// state로 지정할 변수가 여러 개일 경우, useState ()를 변수 수 만큼 실행해야 된다.
// useEffect : 클래스 컴포넌트에 존재하는 생명주기 함수를 사용할 수 있도록 한다.
// 사용법 :
// useEffect(이펙트함수, 의존성 배열); //이펙트함수 = 콜백함수
// 의존성 배열 : 콜백함수인 이펙트 함수가 의존하고 있는 배열, 해당 의존성 배열 안에 있는 변수 중 하나라도
// 값이 변경되면 실행이 된다.
// 이펙트 함수는 기본적으로 컴포넌트가 처음 렌더링된 후 데이터 업데이트에 의한 재렌더링 시 실행
// 이펙트 함수를 마운트, 언마운트 시 각각 1 번만 실행하고자 할 경우 의존성 배열에 빈배열을 사용(componentDidMount, componentWillUnmount 기능 수행)
// 의존성 배열이 없을 경우, 화면이 재랜더링 된 이후에 실행(componentDidUpdate 기능 수행)
// 훅의 규칙 :
// 1. 훅은 무조건 최상의 레벨에서만 호출
// if, for문 내부에서 훅을 호출하면 안됨
// 2. 훅은 컴포넌트가 렌더링 될 때 마다 매번 같은 순서로 호출되어야 함
// 3. 함수 컴포넌트에서만 훅을 사용할 수 있음
// 일반적인 자바스크립트 함수에서 훅을 호출하면 안됨
// 함수 컴포넌트 사용
function CountButton2(props){
// 지역변수
// let count = 0;
//useState를 통해서 state객체에 추가함
const [count, setCount] = useState(0); //setCount를 통해서만 수정가능하게. 초기값은 0으로 지정.
// 화면이 리로딩 될 때마다 업데이트된다
// useEffect를 사용하여 componentDidMount, componentDidUpdate를 구현함
useEffect(()=>{
document.title = `총 ${count}회 클릭했습니다.`
}, [count]); //deps : [count] 카운트가 변경할 때만 동작
// 함수 선언
const countUp = () => {
// count++; 여기서 아래처럼 바뀌었다.
setCount(count+1);
console.log(`count : ${count}`);
}
const countDown = () => {
// count--;
// setCount(count-1);
//이렇게 콜백함수 형태로 사용해도 된다.
setCount((count)=>{
return count-1;
});
console.log(`count : ${count}`);
}
return (
<div>
<label className = {"form-label"}>count : {count}</label><br />
<button className={"btn btn-primary"} onClick={countUp}> + </button>
<button className={"btn btn-success"} onClick={countDown}> - </button>
</div>
)
}
export default CountButton2;
UseCounter.jsx,Accommodate.jsx 생성
// UseCounter.jsx
// 훅 두 번 째 예제
import React,{useState} from "react";
// 커스텀 훅
// 이름에 use를 접두사로 사용
// 매개변수, 반환값을 사용자 마음대로 설정
// 내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함
// 커스텀 훅으로 설정
// 매개변수를 마음대로 설정함
function useCounter(initValue){ //porps대신 initValue 사용한 이유는 커스텀 훅이니까
// state 사용을 위해서 useState 설정 함 initValue이 자리에 초기값 넣어줌
const [count, setCount] = useState(initValue);
// 함수 실행 시 state를 수정하기 위한 setCount를 실행 아래가 원본인데 축약 가능. setCount되면 화면이 재랜더링된다
const increaseCount = () => {
setCount((count)=> {
return count + 1
});
}
const decreaseCount = () =>{
// 자바스크립트에서 지원하는 수학 클랫의 max 함수를 사용하여 0이하의 값을 사용할 수 없도록 제약 // Math.max : 최댓값
setCount((count)=> Math.max(count-1,0)); //0밑으로 안내려가게. 0이하로 못 내려간다
// 이렇게 해도 된다. 위의 것은 더 심플하다.
// setCount((count)=>{
// count--;
// if (count < 0) {
// count = 0;
// }
// return count;
// })
}
return [count, increaseCount, decreaseCount];
}
export default useCounter;
// Accommodate.jsx
import React,{useState, useEffect} from "react";
import useCounter from "./UseCounter";
const MAX_CAPACITY = 10; //최댓값
function Accommodate(props){
// state를 사용하기 위해 useState()를 설정
const [isFull, setIsFull] = useState(false); //기본값은 false, 수정할려면 setIsFull을 사용해야한다
// 해당 컴포넌트 내부에서 생성해야 할 state 객체 및 setState()를 커스텀 훅을 통해서 생성함
// 커스텀 훅을 사용했기 때문에 재활용까지 가능함.
const [count, increaseCount, decreaseCount] = useCounter(0);
//원래는 아래처럼 직접 다 만들어야 했어야 했음
// const [count ,setCont] = useState(0);
// const increaseCount=( )=> {
//
// }
// 커스텀 훅
// 리액트에서 사용하는 훅이 아닌 사용자가 필요에 의해서 생성하여 사용하는 훅
// 이름에 접두사로 use를 사용하고 함수 내부에서 다른 훅을 호출하는 단순 자바스크립트 함수
// 파라미터 및 반환값을 사용자가 직접 지정할 수 있음
// 중복되는 로직을 커스텀 훅으로 설정하여 재사용을 하기 위함
// 이름의 접두사로 use를 사용하지 않을 경우 함수 내부에서 훅을 사용하는지 판단할 수 없음
// 리액트 생명주기 함수를 사용하기 위해서 useEffect를 설정함.
// 의존성 배열이 없을 경우 componentDidMount, componentWillUnmount를 실행하는 것과 같은 효과
useEffect(()=>{
console.log("============");
console.log("useEffect() is called");
console.log(`isFull : ${isFull}`);
},[]);
// useEffect 두 번째 사용되고 있음. 반복가능
// 의존성 배열에 count를 설정하여 count값이 수정되면 componentDidUpdate를 실행하는 것과 같은 효과
useEffect(() => {
setIsFull(count >= MAX_CAPACITY);
console.log("현재 count 값 : " + count);
}, [count]);
return(
<div>
{/*현재 state로 설정된 count의 값을 출력*/}
<p>{`총 ${count}명 수용했습니다.`}</p>
{/*커스텀 훅을 통해 만들어진 사용자 입장 / 퇴장 함수를 버튼에 등록 */}
<button onClick={increaseCount} disabled={isFull} className={"btn btn-success"}>입장</button>
<button onClick={decreaseCount} className = {"btn btn-primary" }>퇴장</button>
{/* if문을 사용하지 못하기 때문에 삼항연산자 등을 사용해야한다.*/}
{/* &&일 때 앞의 값이 false라면 뒤의 것 자체 실행하지 않는다. 랜더링 자체가 되지않음 ->console창에 아무것도 뜨지 않음
true,true 여야지만 안의 내용이 출력된다*/}
{isFull && <p style={{color :"red"}}>정원이 가득찼습니다</p>}
</div>
)
}
export default Accommodate;
문제 1) 숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를
출력하는 리액트 컴포넌트를 작성하세요
// Calculator.jsx
import React, {useState} from "react";
function Calculator(props){
// const [result, setResult] = useState(0);
// const
// const resultPlus = ()=>{
// setResult()
// }
// 계산된 데이터를 저장하고 있을 변수(선생님 버전)
let calResult = 0; //바깥에 있어야함
const[num1, setNum1] = useState(0);
const[num2, setNum2] = useState(0);
const [result, setResult] = useState(0);
// '='기호 사용해보기
const [finalResult, setFinalResult] = useState(0);
// const resultCal = () =>{}
const plus = () => {
setFinalResult(parseInt(num1) + parseInt(num2));
console.log(`덧셈 결과 : ${finalResult}`);
// const number1 = parseInt(num1);
// const number2 = parseInt(num2);
// calResult = number1 + number2;
}
const minus = () => {
setFinalResult(num1 - num2);
console.log(`뺄셈 결과 : ${finalResult}`)
}
const multiply = () => {
setFinalResult(num1 * num2);
console.log(`곱셈 결과 : ${finalResult}`)
}
const divisioin = () => {
setFinalResult(num1 / num2);
console.log(`나눗셈 결과 : ${finalResult}`)
}
const equal = () => {
setResult(finalResult);
}
return(
<div>
<br />
<label className = {"form-label"} >숫자 2개를 입력하고 연산 버튼을 눌러주세요</label><br />
<input className={"text m-3"} id={"num1"} name={"num1"} onChange={(e)=>setNum1(e.target.value)}/>
<input className={"text"} id = {"num2" } name={"num2"} onChange={(e)=>setNum2(e.target.value)}/><br />
<button className={"btn btn-primary m-3"} onClick={plus} > + </button>
<button className={"btn btn-success m-3"} onClick={minus}> - </button>
<button className={"btn btn-success m-3"} onClick={multiply}> * </button>
<button className={"btn btn-success m-3"} onClick={divisioin}> / </button>
<button className={"btn btn-success m-3"} onClick={equal}> = </button><br />
<label className = {"form-label" }>결과값</label><br />
<input className = {"text" } id={"result"} value = {result}></input>
</div>
)
}
export default Calculator;
이벤트 관련된 엄청 많은 정보를 알려준다.
// Events.jsx
// 리액트는 html과 같은 이벤트를 가지고 있다.
// 카멜명명법을 사용하므로 onclick = "sum()" 에서 onClick={sum}으로 변경하여 사용한다.
// 매개변수 전달 시 이벤트 부분에 화살표 콜백 함수를 사용한다.
// onClick = {() => sum(10)} 이런 형태로 사용된다
// 이벤트 사용 시 이벤트 헨들러도 매개변수로 전달이 가능하다.
// onClick = {(event) => sum(10, event)}
import React from "react";
// 기본 함수를 클릭이벤트와 연동
const click1 = () => alert("일반 클릭 이벤트");
// 매개변수가 있는 함수를 클릭이벤트와 연동
const click2 = (item) => alert(`매개변수 값 : ${item}, \n매개변수가 있는 이벤트`)
// 매개변수로 이벤트 핸들러(=이벤트 객체)를 사용하는 함수를 클릭이벤트와 연동
// 이벤트 객체는 어느 객체에서 시작하는지 확인 가능하다.
// 어떤 키로 작동되었는지 (ex. 키보드의 어느 키, 마우스 클릭 등) 확인 가능하다.
const click3 = (item, event) => { //event : 자바스크립트가 원래 가지고 있는 이벤트 객체
let msg = `매개변수와 event 객체가 있는 클릭 이벤트
매개변수 값 : ${item}, 이벤트 객체 : ${event.type}`;
console.log(event);
alert(msg);
}
function Events(){
return(
<div>
<button type={"button"} className={"btn btn-primary"} onClick={click1}>일반 클릭 이벤트</button>
{/*함수이름쓰기 + 매개변수 적기*/}
<button type={"button"} className={"btn btn-success"} onClick = {() => click2(100)}>매개변수가 있는 클릭 이벤트</button>
{/*콜백함수에 event넣어준다. -> 내 버튼에서 발생한 이벤트 객체. 그걸 매개변수로 넘겨주는 것.*/}
<button type={"button"} className={"btn btn-info"} onClick = {(event)=>click3(200,event)}>event 객체가 있는 클릭 이벤트</button>
</div>
)
}
export default Events;
클릭하면 아래 처럼 확인 완료로 바뀐다
시간 설정한 만큼 뒤에 다시 돌아온다
// ConfirmButton.jsx
import React,{useState} from "react";
function ConfirmButton(props){
const [isConfirmed, setIsConfirmed] = useState(false);
// setIsConfirmed -> state 수정
const handleConfirm = () => {
//isConfirmed 콜백 함수 동작. 현재값 false에 not을 붙으면 true로 값이 들어감
setIsConfirmed((isConfirmed) => !isConfirmed);
setTimeout(()=>{
setIsConfirmed(false);
},2000); // 2초 후에 돌아온다
};
return(
<div>
{/*값이 true이면 disabled가 동작됨 => 버튼 사용할 수 없게 만듦*/}
<button type={"button"} className={"btn btn-primary"} onClick={handleConfirm} disabled={isConfirmed}>
{isConfirmed ? "확인 완료" : "확인하기"}
</button>
</div>
)
}
export default ConfirmButton;