setState()
: state 의 값을 변경하는 함수컴포넌트가 생성되고 내용이 변경되고 컴포넌트가 삭제되는 상태
folder3/LinkButton.js
// folder3/LinkButton.js
import React from "react";
class LinkButton extends React.Component {
constructor(props) {
super(props);
this.state = {
linked: false,
}
this.setState({
linked: true,
});
}
}
folder3/Notification.jsx
// folder3/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;
folder3/NotificationList.jsx
// folder3/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: [],
};
}
// 컴포넌트가 마운트된 후 실행되는 생명주기 함수
componentDidMount() {
// object 타입을 사용한 확장표현식. 현재는 비어있음. 타이머가 돌면서 값 들어갈 예정
const { notification } = this.state;
// 타이머를 사용하여 지정된 시간을 간격으로 반복 실행 (1000 = 1초)
timer = setInterval(() => {
// 첫번째 : 0 < 3 (true)
// 두번째 : 1 < 3 (true)
// 지연 변수인 배열 notification 크기가 전연 변수인 배열 msgData 의 크기보다 작을 경우 실행되는 코드
if (notification.length < msgData.length) {
const index = notification.length;
// msgData[0] 값 push
// msgData[1] 값 push
notification.push(msgData[index]);
// setState() 함수를 사용하여 state 객체의 notification 을 방금 업데이트한 값으로 수정
this.setState({
// 전역변수로 선언된 notification : 변수로 선언한 notification
notification: notification,
});
}
else {
// timer 삭제
clearInterval(timer);
}
// 1초 후 다시 실행
}, 1000);
}
// 화면을 생성(렌더링)하는 함수
render() {
return (
<div>
{/* map : ES6 버전에서 추가된 배열 관련 함수, 지정한 배열의 크기만큼 반복하고 결과를 배열로 반환하는 함수 */}
{this.state.notification.map((item) => {
return <Notification key={item.id} message = {item.message} />;
})}
</div>
);
}
}
export default NotificationList;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import App2 from "./App2";
import App3 from "./App3";
import App4 from "./App4";
import App5 from "./App5";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
// strictMode : 자바스크립트의 문법을 엄격하게 확인한다는 의미
// React.strictMode : 배포 버전에는 제외되고 개발 버전에서만 동작하는 엄격 모드, 몇 가지 함수를 중복 실행하여 잘못된 것이 없는지 개발자에게 확인하도록 함(생략가능)
<React.StrictMode>
{/* <App />*/}
{/* <App2 />*/}
{/* <App3 />*/}
{/* <App4 />*/}
<App5 />
</React.StrictMode>
);
reportWebVitals();
countButton.jsx
// folder3/CountButton.jsx : setState() 익히기
import React from "react";
import Button from "react-bootstrap/Button";
import {logDOM} from "@testing-library/react";
class CountButton extends React.Component {
constructor(props) {
super(props);
// state 객체 생성
this.state = {
count: 0,
}
// 객체 멤버 변수 count 선언
this.count = this.state.count;
}
// 메서드 2개 생성
countUp = () => {
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 = () => {
console.log("이전 this.count : " + this.count);
this.count = this.count - 1
this.setState({
count: this.count
});
console.log("- 사용 후 this.count : " + this.count);
};
render() {
return (
<div>
<label htmlFor="" 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;
기존의 존재하는 기능을 추가로 함께 사용하도록 하는 것, state 와 리액트 생명주기 기능을 원하는 시점에 실행할 수 있도록 하는 기능
훅을 사용 시 미리 해당 함수를 import 해서 사용해야 함
useState
: state 객체를 함수 컴포넌트에서 사용할 수 있도록 하는 기능useEffect
: useState() 와 함께 가장 많이 사용되는 훅, 리액트 생명주기 함수인 componentDidMount, componentDidUpdate, componentWillUnmount 의 기능을 하나로 합한 기능useMemo
: 재 렌더링 시 연산량이 높은 작업을 반복하는 것을 피할 수 있게 하는 기능 (거의 안씀)useCallback
: useMemo 와 비슷한 기능, 값이 아닌 함수를 반환함 (거의 안씀)useRef
: 지정한 컴포넌트에 접근할 수 있는 객체 (거의 안씀)클래스 컴포넌트에 존재하는 state 객체를 함수 컴포넌트에서 사용할 수 있도록 함
const[변수명, set함수명] = useState(초기값);
변수명
: state 로 지정할 지역변수명
set함수명
: state 로 지정된 변수명에 접두사 set 을 붙여서 사용, 카멜명명법 사용
useState(초기값)
: 확장표현식을 통해서 지정한 변수를 state 객체로 설정함, 초기값은 state 로 설정된 변수의 초기값을 말함
state 로 지정할 변수가 여러개일 경우 useState() 를 변수의 수 만큼 실행해야 함
클래스 컴포넌트에 존재하는 생명주기 함수를 사용할 수 있도록 함
사용법
useEffect(이펙트함수, 의존성배열);
이펙트 함수는 기본적으로 컴포넌트가 처음 렌더링된 후 데이터 업데이트에 의한 재렌더링 시 실행됨
이펙트 함수를 마운트, 언마운트 시 각각 1번씩만 실행하고자 할 경우 의존성 배열에 빈 배열을 사용(componentDidMount, componentWillUnmount 기능 수행)
콜백 함수인 이펙트 함수가 의존하고 있는 배열, 해당 의존성 배열 안에 있는 변수 중 하나라도 값이 변경되면 실행
의존성 배열이 없을 경우 화면이 재 렌더링 된 이후에 실행(componentDidUpdate 기능 수행)
Hook 의 규칙 :
1. Hook은 무조건 최상위 레벨에서만 호출 (if, for 문 내부에서 훅을 호출하면 안됨)
2. Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 함
3. 함수 컴포넌트에서만 Hook을 사용할 수 있음
일반적인 자바스크립트 함수에서 Hook을 호출하면 안됨
리액트에서 제공하는 훅이 아닌 사용자가 필요에 의해서 생성하여 사용하는 훅
이름에 접두사로 use 를 사용
하고, 함수 내부에서 다른 훅을 호출하는 단순 자바스크립트 함수
파라미터 및 반환값을 사용자가 직접 지정할 수 있음
중복되는 로직을 커스텀 훅으로 설정하여 재사용하기 위함
이름의 접두사로 use
를 사용하지 않을 경우 함수 내부에서 훅을 사용하는지 판단할 수 없음
folder3/CountButton2.jsx
// folder3/CountButton2.jsx : hooks 익히기
// useState() 훅을 사용하기 위해서 미리 import
import React, {useState, useEffect} from "react";
// 함수 컴포넌트 사용
function CountButton2(props) {
// 지역변수
// let count = 0;
// useState 를 통해서 state 객체에 추가함
const [count, setCount] = useState(0);
// [deps] 값 안줬으면 컴포넌트 DidMount 될때마다 동작, [deps] 값을 [count]로 줬으면 count 값이 변경될때마다 동작
// useEffect 를 사용하여 componentDidMount, componentDidUpdate 를 구현함
useEffect(() => {
document.title = `총 ${count}회 클릭했습니다.`
}/*[count]*/);
// 함수 선언
const countUp = () => {
// count++; <- 지역변수 사용했을때 코드.(주석처리)
setCount(count + 1);
console.log(`count++ : ${count}`);
}
const countDown = () => {
// count--; <- 지역변수 사용했을때 코드.(주석처리)
// setCount(count - 1);
// 콜백함수의 형태도 사용 가능
setCount(() => {
return count - 1;
});
console.log(`count-- : ${count}`);
}
return (
<div>
<label htmlFor="" className={"form-label"}>count : <span>{count}</span></label><br/>
<button className={"btn btn-outline-primary"} onClick={countUp}> + </button>
<button className={"btn btn-outline-danger"} onClick={countDown}> - </button>
</div>
);
}
export default CountButton2;
이름에 use 를 접두사로 사용
매개변수, 반환값을 사용자 마음대로 설정
내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함
props 가 아닌 initValue 인 이유 : 커스텀 훅이라서
folder3/UseCounter.jsx
// folder3/UseCounter.jsx : 커스텀 훅
import React, {useState} from "react";
// 커스텀 훅
// 이름에 use 를 접두사로 사용
// 매개변수, 반환값을 사용자 마음대로 설정
// 내부에서 훅을 사용한 함수이며, 소스코드 재활용을 위해서 사용함
// props 가 아닌 initValue 인 이유 : 커스텀 훅이라서
// 커스텀 훅으로 설정
// 매개변수를 마음대로 설정함
function UseCounter(initValue) {
// state 사용을 위해서 useState 설정을 걸어줌
const [count, setCount] = useState(initValue);
// 함수 실행 시 state 를 수정하기 위함 setCount 를 실행
const increaseCount = () => {
// 자바스크립트에서 지원하는 수학 클래스의 max 함수를 사용하여 0 이하의 값을 사용할 수 없도록 제약을 걸어놓음
setCount((count) => {return count + 1});
}
const decreaseCount = () => {
setCount((count) => Math.max(count - 1, 0));
}
// math 사용한 코드와 동일
// setCount((count) => {
// count--;
// if (count < 0) {
// count = 0;
// }
// return count;
// });
return[count, increaseCount, decreaseCount];
}
export default UseCounter;
Accomodate.jsx
// folder3/Accommodate.jsx : 커스텀 훅
import React, {useState, useEffect} from "react";
// 커스텀 훅 (useCounter)
import useCounter from "./UseCounter";
const MAX_CAPACITY = 10; // 최대값
function Accommodate(props) {
// state 를 사용하기 위해 useState() 를 설정
const [isFull, setIsFull] = useState(false);
// 원래 코드 :
// const[count, setCount] = useState(0); -> 하위에 함수 만들어주고....
// 해당 컴포넌트 내부에서 생성해야 할 state 객체 및 setState 를 커스텀 훅을 통해서 생성함
// 커스텀 훅을 사용했기 때문에 재활용이 가능함
const [count, increaseCount, decreaseCount] = useCounter(0);
// 리액트 생명주기 함수를 사용하기 위해서 useEffect 를 설정함
// 의존성 배열이 없을 겨우 componentDidMount, componentWillUnmount 를 실행하는 것과 같은 효과
useEffect(() => {
console.log("====================");
console.log("useEffect() is called");
console.log(`isFull : ${isFull}`);
}, []);
// 의존성 배열에 count 를 설정하여 count 값이 수정되면 componentDidUpdate 를 실행하는 것과 같은 효과 발생
useEffect(() => {
setIsFull(count >= MAX_CAPACITY);
console.log("현재 count값 : " + count);
}, [count]);
return (
<div>
{/* 현재 state 로 설정된 count 의 값을 출력 */}
<p>{`총 ${count}명 수용했습니다.`}</p>
{/* 커스텀 훅을 통해서 만들어진 사용자 입장/퇴장 함수를 버튼에 등록 */}
<button className={"btn btn-warning"} onClick={increaseCount} disabled={isFull}>입장</button>
<button className={"btn btn-info"} onClick={decreaseCount}>퇴장</button>
{/* isFull 이 true 면 뒤에거 확인(실행), isFull 이 false 라면 && 기호 뒤에는 아예 읽지도 않음 = 렌더링자체가 안됨 */}
{isFull && <p style={{color: "red"}}>정원이 가득찼습니다.</p>}
</div>
)
}
export default Accommodate;
숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를 출력하는 리액트 컴포넌트를 작성하세요
folder/Calculator.jsx
// folder/Calculator.jsx
/*
문제1) 숫자 2개를 입력받아 계산 기호 버튼에 따라서 결과를 출력하는 리액트 컴포넌트를 작성하세요
- input 태그를 통해서 숫자를 입력(태그이름 : num1, num2, result)
- 버튼 5개 ( + - * / = )
- 컴포넌트 이름 : Calulator.jsx
- useState 총 3개 사용 (함수 컴포넌트로 작성)
*/
import React, {useState} from "react";
let calResult = 0;
function Calculator(props) {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
const [result, setResult] = useState(0);
const plus = () => {
const number1 = parseInt(num1);
const number2 = parseInt(num2);
calResult = number1 + number2;
}
const minus = () => {
const number1 = parseInt(num1);
const number2 = parseInt(num2);
calResult = number1 - number2;
}
const multiply = () => {
const number1 = parseInt(num1);
const number2 = parseInt(num2);
calResult = number1 * number2;
}
const division = () => {
const number1 = parseInt(num1);
const number2 = parseInt(num2);
calResult = number1 / number2;
}
const equal = () => {
setResult(calResult);
}
return (
<div>
<hr/>
<div className={"my-3 text-center"}>
<span>숫자1 </span><input type="text" value={num1} onChange={(e) => (setNum1(parseInt(e.target.value)))}/>
<span className={"ms-3"}>숫자2 </span><input type="text" value={num2} onChange={(e) => (setNum2(parseInt(e.target.value)))}/>
</div>
<div className={"text-center"}>
<button onClick={plus} className={"btn btn-info me-5"}> + </button>
<button onClick={minus} className={"btn btn-info me-5"}> - </button>
<button onClick={multiply} className={"btn btn-info me-5"}> * </button>
<button onClick={division} className={"btn btn-info me-5"}> / </button>
<button onClick={equal} className={"btn btn-success"}> = </button>
</div>
<div className={"text-center mt-4"}>
<span>결과 = </span><input type="text" value={result}/>
</div>
</div>
);
}
export default Calculator;
리액트는 html 과 같은 이벤트를 가지고 있음
카멜명명법을 사용하므로 onclick="sum()"
=> onClick={sum}
으로 변경되어 사용
onClick={() => sum(10)}
onClick={(event) => sum(10, event)}
folder3/Events.jsx
//folder3/Events.jsx
import React from "react";
function Events() {
// 기본 함수를 클릭이벤트와 연동
const click1 = () => alert("일반 클릭 이벤트");
// 매개변수가 있는 함수를 클릭이벤트와 연동
const click2 = (item) => alert(`매개변수 값 : ${item}, \n매개변수가 있는 이벤트`);
// 매개변수로 이벤트 핸들러(= 이벤트 객체)를 사용하는 함수를 클릭이벤트아 연동
const click3 = (item, event) => {
alert(`매개변수와 event 객체가 있는 클릭 이벤트
매개변수 값 : ${item}, 이벤트 객체 : ${event.type}`);
}
return (
<div>
<button type={"button"} className={"btn btn-primary"} onClick={click1}>일반 클릭 이벤트</button>
<button type={"button"} className={"btn btn-success"} onClick={() => click2(100)}>매개변수가 있는 클릭 이벤트</button>
<button type={"button"} className={"btn btn-info"} onClick={(event) => click3(200,event)}>event 객체가 있는 클릭 이벤트</button>
</div>
);
}
export default Events;
folder/ConfirmButon.jsx
// folder/ConfirmButon.jsx
import React, {useState} from "react";
function ConfirmButton(props) {
const [isConfirmed, setIsConfirmed] = useState(false);
const handleConfirm = () => {
setIsConfirmed((prevConfirmed) => !isConfirmed);
setTimeout(() => {
setIsConfirmed(false);
}, 1000)
};
return (
<div>
<button type={"button"} className={"btn btn-primary"} onClick={handleConfirm} disabled={isConfirmed}>
{isConfirmed ? "확인 완료" : "확인 하기"}
</button>
</div>
);
}
export default ConfirmButton;
App5.jsx
// App5.jsx
import React from "react";
import NotificationList from "./folder3/NotificationList";
import CountButton from "./folder3/CountButton";
import CountButton2 from "./folder3/CountButton2";
import Accommodate from "./folder3/Accommodate";
import Calculator from "./folder3/Calculator";
import Events from "./folder3/Events";
import ConfirmButton from "./folder3/ConfirmButton";
function App5() {
return (
<div className={"container"}>
<NotificationList/>
<CountButton/>
<CountButton2/>
<Accommodate/>
<Calculator/>
<br/>
<Events/>
<br/>
<ConfirmButton/>
</div>
);
}
export default App5;