import { useCallback } from "react";
// 컴포넌트 외부에 함수 선언
function handleClick2(e) {
console.log("click2", e);
}
function App() {
/**
* 합성 이벤트 (SyntheticEvent)
* - React에서 이벤트가 발생할 때, 이벤트 핸들러의 인자로 합성 이벤트 객체가 전달됨
* - 이 합성 이벤트는 javascript에서 전달 받는 이벤트 객체를 확장(래핑)한 객체임
* (거의 동일한 인터페이스를 가지고 있음)
* - 원본 이벤트 객체(native event)는 syntheticEvent.nativeEvent 에 있음
* - 지금은 그냥 같은 이벤트 객체라고 생각해도 무방
*/
function handleClick1(e) {
console.log("click1", e);
}
/**
* 이벤트 핸들러(함수)를 만들 때는 react lifecycle을 고려하자!
* - 컴포넌트가 리랜더링 되면 컴포넌트 내에서 단순 정의한 함수가 새로운 함수로 만들어짐
* - 이것은 불필요한 작업으로 성능 문제를 야기함
* - 그래서 함수의 정의를 최대한 컴포넌트 밖으로 빼거나,
* useCallback으로 감싸줘서 매 랜더링 마다 새로 만들어지지 않도록 해줄 필요가 있음
*/
const handleChange = useCallback((e) => {
console.log("change", e.target.value);
}, []);
return (
<div>
<button onClick={handleClick1}>Button1</button>
<button onClick={handleClick2}>Button2</button>
<div>
<input type="text" onChange={handleChange} />
</div>
</div>
);
}
/**
* useState: 상태 값과 그 값을 갱신하는 함수를 반환
* - 인자: 초기 상태 값
* - 반환: [상태 변수, 상태에 대한 Setter]
*/
const [value, setValue] = useState(0);
useEffect
/**
* useEffect: 컴포넌트가 렌더링 될 때, 특정 작업을 실행
* - 인자
* - 실행하고자 하는 함수 (effect callback)
* - effect는 정리(clean-up) 함수를 반환할 수 있음
* - 반환된 함수는 컴포넌트가 언마운트 또는 effect 재실행 이전에 실행됨
* - 의존성 배열 (dependency list)
*/
useEffect(() => {
console.log("[Function] useEffect []: 컴포넌트가 마운트 될 때, 한 번만!");
//마운트가 됐을 때! eventListener를 넣기 (밖에 두면 이벤트가 중복돼서 만들어짐)
//document.body.addEventListener('click', ()=>{console.log('click body');});
//언마운트가 됐을 때 남아있는 리소스를 제거해야 이벤트가 발생하는 문제를 해결할 수 있음!!
const eventHandler = () => {console.log('click body')};
document.body.addEventListener('click', eventHandler);
return () => { //cleanup함수라고 함.
console.log("[Function] useEffect return []: 컴포넌트가 언마운트 될 때,");
document.body.removeEventListener('click', eventHandler);
};
}, []);
useEffect(() => {
console.log(
"[Function] useEffect [value]: 컴포넌트가 마운트 될 때, + value가 변경되면,"
);
return () => { //cleanup함수라고 함.
console.log(
"[Function] useEffect return [value]: 새로 useEffect를 수행하기 전에,"
);
};
}, [value]); //두번째 인자에 이렇게 넣고, value가 변경될 때 실행하도록할 수 있음
//value가 변경되고 -> 다시 랜더링되면서 컴포넌트의 첫줄부터 시작 -> useEffect return(cleanup함수) ->
//useEffect의 첫줄 실행되는 순으로 진행됨.
useCallback
- 성능을 위해서
/**
* useCallback: 메모이제이션된 콜백을 반환
* - 인자
* - 메모이제이션 할 함수
* - 의존성 배열
* - 반환: 메모이제이션 된 함수
* *의존성 배열을 제대로 셋팅하지 않으면 함수 안에서 사용되는 값이 업데이트 되지 않은 값일 수 있음
*/
//[비교] useCallback 없이 사용할 때, 리랜더링될 때마다 아래 함수를 계속 만들어줌 ... 가비지컬랙터가 작동된다고 해도 중복해서 만드는 것은 성능에 좋지 않다.
const increaseValue = () => {setValue(value+1);};
//[비교] useCallback, 처음에 랜더링됐을 때 한번 만들어놨으면 두번째 랜더링에 와서 기존에 있던 걸 기억해서 재활용하는 식으로 동작
const increaseValue = useCallback(
() => {setValue(value + 1);}, [value] //value 값이 변할 때, 함수 만들도록 함. value가 없다면 1.. (처음 value값이 0일 때.. 계속 재활용하면 1로 세팅)
);
//사실 위 상황은 useCallback이 필요한 상황이 아님. (예시를 위한 것일 뿐)
const resetValue = useCallback(() => { setValue(0); }); //value와 같은 디펜던시가 없음. 처음 만든 함수를 계속 재활용해도 된다는 소리임.
return (
<div>
<h1>value: {value}</h1>
<button onClick={increaseValue}>Increase value</button>
</div>
);
Hooks를 사용할 때 주의사항 - 조건문 안에 사용해서는 안 된다. - React 컴포넌트에서만 사이클 관리를 위해 쓰이는 것이기 때문에 함수 안에 사용해서도 안 된다.
//App.js
export default function App() { //export default 다른 컴포넌트에서 사용하기 위함.
return (
<div>
<h1>Hello,</h1>
<h2>World</h2>
</div>
);
}
-------------------------
//index.js
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />, //App 컴포넌트
document.getElementById('root');
);
---------------------------
컴포넌트 만들기
- JSX 형태로만 컴포넌트를 만드는 것 -> 함수형태로 컴포넌트를 만드는 것
: 함수라는 scope 개념이 생기면서 고유한 로직이 들어갈 수 있고, 컴포넌트 스스로 상태를 가질 수 있음.
- import, export 문법 사용 (es6에서 모듈을 불러오고 내보내는 방법)
- 컴포넌트 이름은 PascalCase로 지어야 함 (단어의 시작 문자를 대문자)
- 컴포넌트는 의미단위로 쪼개서 파일을 분리
Props
Props란? - 부모 컴포넌트에서 자식 컴포넌트로 내려주는 데이터
// 부모에서 자식으로 데이터를 내려줌.
function App() { //부모
return (
<div>
<MyComponent value={'test'}/>
</div>
);
}
function MyComponent(props) { //자식
return <div>{props.value}</div>;
}
--------------------------------------
// 컴포넌트 태그로 감싼 값이 props.children으로 전달됨.
function App() {
return (
<div>
<MyComponent>
<h1>value</h1>
</MyComponent>
</div>
);
}
function MyComponent(props) {
return <div>{props.children}</div>; //<h1>value</h1>
}
Props 활용 팁
- 구조분해할당 구문을 잘 활용하자
- 특정 Props에 기본 값을 줄 수 있음
- Props는 읽기 전용
//Heading.js
export default function Heading(props) {
if (props.type === "h2") {
return <h2>{props.children}</h2>;
}
return <h1>{props.children}</h1>;
}
//index.js
import Heading from "./components/Heading";
export default function App() {
return (
<div>
<Heading type="h1">test</Heading>
<Heading type="h2">World</Heading>
</div>
);
}
State
State란 - 컴포넌트(상태를 관리) 내부에서 사용되는 변수
- State 값이 변하면 컴포넌트가 리렌더링 됨 - 렌더링 사이클에서 값이 보존됨
import { useState } from "react";
export default function App() {
let [value, setValue] = useState(0);
return (
<div>
<h1>value: {value}</h1>
<button
onClick={() => {
console.log("Increase value1", value);
setValue(value + 1);
console.log("Increase value2", value);
//위 아래 콘솔 로그 모두 같은 값으로 찍힘(증가 전 값으로 나타남): 이유는 모든 랜더링이 끝난 후 한꺼번에 모아서 setState으로 변경을 해주기 때문.!!
}}
>
Increase value
</button>
);
}
클래스형 컴포넌트 vs. 함수형 컴포넌트 (위)
클래스형 컴포넌트
- 클래스 문법으로 구현한 컴포넌트
- useStatae와 같은 Hooks는 React 버전 16.8부터 사용 가능 (그 전에는 클래스형 컴포넌트만 state를 가질 수 있었음)
- 클래스의 멤버 변수로 state 정의
- render라는 멤버 함수에서 반환한 값이 화면에 그려짐
import React, { Component } from 'react';
export default class App extends Component {
state = {
value: 0
};
constructor(props) {
super(props);
this.state = {
value: 1
};
}
resetValue() {
this.setState({ value: 0 });
}
render() {
return (
<div>
<h1>value: {this.state.value}</h1>
<button
onClick={() => {
this.setState((state) => ({ //Component의 함수 setState : 컴포넌트에 값이 바뀌었다고 알림. -> render 함수 다시 실행
value: state.value + 1
}));
}}
>
Increase value
</button>
<button
onClick={this.resetValue.bind(this)} //bind 안쓰면 this를 button 요소를 가리켜 오류.
>
Reset value
</button>
</div>
);
}
}