React JS 컴포넌트와 랜더링, State, Props

Renee·2022년 9월 21일
0
post-thumbnail

React JS?

What is React JS?

Interactive UI를 만들어주는 Javascript library
React JS는 새로운 데이터가 들어올 때마다 자동으로 UI를 refresh하고, 기능을 단위별로 캡슐화한 component를 기반으로 작동한다.

- Component?

React 컴포넌트는 render()라는 메서드를 구현하는데, 이 메서드는 데이터를 입력받아 화면에 표시할 내용을 반환하는 역할을 한다.
사용자는 컴포넌트를 사용하여 React에게 화면에 표시하고 싶은 것이 무엇인지 알려주고, 이러한 데이터가 변경될 때마다 React는 컴포넌트를 효율적으로 업데이트하고 다시 랜더링한다.
(컴포넌트로 전달된 데이터는 render() 안에서 this.props를 통해 접근할 수 있다.)

- CDN 방식으로 React 사용하기

CDN Links

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script>
	const root = document.getElementByID("root");
	const h3 = React.createElement(
		"h3", 
		{onMouseEnter: () => console.log("Mouse enter"},
		"I'm a h3"
	);
	const btn = React.createElement(
		"button", 
		{onClick: () => console.log("Clicked"}, 
		"Click Me!"
	);
	const container = React.createElement("div", null, [h3, btn]);
	ReactDOM.render(container, root);
</script>
</html>

💡 React JS가 엔진이라면, React DOM은 React Element들로 생성한 HTML을 body에 배치하는 역할을 한다.

React.createElement()

(Element, Property): React JS로 HTML 요소와 속성을 생성한다.

ReactDOM.render()

(Element, Position): React element를 지정한 위치에 HTML로 만든다.

- JSX

JavaScript를 확장한 문법으로, HTML을 작성하는 방식과 유사하여 가독성이 좋다.
JSX로 적은 코드를 브라우저가 이해할 수 있는 형태로 변환하기 위해 Babel을 설치해야 한다.

Babel Standalone 바로가기

<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">

- JSX를 써서 위의 코드와 동일한 HTML을 만들어 보기

✓ const XXX = () => (); 과 function XXX () { return (); } 은 동일한 역할이다.

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	const root = document.getElementByID("root");
	function Title() {
		return (
			<h3 id="title" onMouseEnter={() => console.log("Mouse enter"}}>
				Hello I'm a title
			</h3>
		);
	}
	const Button = () => (
		<button onClick={() => console.log("Clicked"}}>
			Click Me!
		</button>
	);
	const Container = () => (
		<div>
			<Title />
			<Button />
		</div>	
	);
	ReactDOM.render(<Container />, root);
</script>
</html>

❗️ HTML 요소와 구분하기 위해 컴포넌트의 첫 글자는 반드시 대문자여야 한다.(ex. Title, Button)

Hooks API Reference

Hook은 React 16.8에서 새로 추가된 개념이다. Hook을 통해 class를 작성하지 않고도 state와 같은 React 기능들을 사용할 수 있다. 기본 hook에는 useState, useEffect, useContext 가 있다.

Component State

동일한 데이터에 대한 변경사항을 여러 컴포넌트에 반영해야 될 때가 있다. 이러한 각각의 변경사항을 "state"라고 한다.

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	const root = document.getElementByID("root");
	let counter = 0;
	function countUp() {
		counter = counter + 1;
		render();
	}
	function render() {
		ReactDOM.render(<Container />, root);
	}
	const Container = () => (
		<div>
			<h3>Total Clicks : {counter}</h3>
			<button onClick={countUp}>Click Me!</button>
		</div>	
	);
	render();
</script>
</html>

위의 코드에서 변수를 JSX 방식으로 전달하는 것을 보면, 중괄호 안에 변수의 이름을 적는 Total Clicks : {counter} 이러한 방식으로 직관적으로 알아차리기 쉽게 되어 있다.

click counter가 업데이트될 때마다 컴포넌트를 재랜더링 하기 위해, Container를 랜더링하는 render()를 만들고, countUp()에 render()을 호출하도록 했다.

그렇다면, React JS는 HTML 전체를 다시 랜더링하는 걸까?

No! 전체를 전부 재생성하는 것이 아니라 바뀐 부분만 새로 생성한다.

- useState()

[state, setState] : 상태 유지 값(state)과 그 값을 갱신하는 함수(setState)를 배열로 리턴함.

✓ useState()를 사용해서 컴포넌트의 state를 변경해보자.

① modifier 함수의 인수로 직접 값을 넣기

const [counter, setCounter] = React.useState(); 로 데이터와 데이터를 변경하는 함수를 각각 "counter"와 "setCounter"로 받는다.

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	const root = document.getElementByID("root");
	
	function App() {
		const [counter, setCounter] = React.useState(0);
		const onClick = () => {
			setCounter(counter + 1);
		};
		return (
		<div>
			<h3>Total Clicks : {counter}</h3>
			<button onClick={onClick}>Click Me!</button>
		</div>	
		);
	}
	ReactDOM.render(<App />, root);
</script>
</html>

이전 state를 바탕으로 현재 state를 설정하는 함수(setCounter())를 쓸 때, 다른 버그로 인해 중간에 값이 바뀌는 문제가 발생할 수 있는데, 이를 방지하기 위해 setCounter()의 인수로 함수를 써서, 반환받은 결과 값으로 state를 갱신하는 방법도 있다. → 이러한 방식을 “함수적 갱신” 이라 하는데, 용어가 중헌게 아니고.. 아래 코드를 보자.

② modifier 함수의 인수로 함수를 넣기

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	const root = document.getElementByID("root");
	
	function App() {
		const [counter, setCounter] = React.useState(0);
		const onClick = () => {
			setCounter((current) => current + 1);
		};
		return (
		<div>
			<h3>Total Clicks : {counter}</h3>
			<button onClick={onClick}>Click Me!</button>
		</div>	
		);
	}
	ReactDOM.render(<App />, root);
</script>
</html>

setCounter((current) => current + 1); → 이러한 방식이 더 안전하다.

- Props

컴포넌트를 정의할 때, 자바스크립트 함수를 사용해서 작성하는 방식이 가장 간단하다.
이러한 방식으로 작성된 컴포넌트를 말 그대로 "함수 컴포넌트" 라고 한다.
함수 컴포넌트는 데이터를 가진 하나의 "props"(속성을 나타내는 데이터) 객체를 인자로 받은 후에 React 엘리먼트를 반환한다.

⬇️ 아래는 DOM 태그만을 사용해서 React 엘리먼트를 나타내는 방법이다.

<script>

	const element = <div />;

</script>

⬇️ React 엘리먼트는 아래와 같이 사용자 정의 컴포넌트로도 나타낼 수 있다.

<script>

	const element = <Welcome name="Sara" />;

</script>

React가 사용자 정의 컴포넌트로 작성한 엘리먼트를 발견하면 JSX attribute와 자식을 해당 컴포넌트에 단일 객체로 전달한다.

이 객체를 "props" 라고 한다.

⬇️ 아래의 코드는 페이지에 "Hello, Jane"을 랜더링 한다.

<script>

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Jane" />;
root.render(element);

</script>

props를 이용해서 동일한 style 요소를 가진 버튼 두 개를 하나의 함수형 컴포넌트로 만들어 보자. ⬇️⬇️

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	function Btn({ what, changeValue }) {
		return <button
			onClick={changeValue}
			style={{
				backgroundColor: "lightpink",
				color: "white",
				padding: "10px 20px",
				border: 0,
				borderRadius: 10
			}}
		>
			{what}
		</button>;
	}
	const MemorizedBtn = React.memo();
	function App() {
		const [value, setValue] = React.useState("Save Changes");
		const changeValue = () => setValue("Revert Changes");
		return (
			<div>
				<MemorizedBtn what={value} changeValue={changeValue} />
				<MemorizedBtn what="Continue" />
			</div>
		);
	}
	const root = document.getElementByID("root");
	ReactDOM.render(<App />, root);
</script>
</html>

PropTypes ?

컴포넌트의 props가 어떤 타입을 받고 있는지 체크한다.
전달받은 데이터가 PropTypes에 일치하지 않으면 콘솔에 경고문이 뜸.

CDN Links

<!-- development version -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>

<!-- production version -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.min.js"></script>

⬇️ 예시) what과 fontSize는 각각 String과 number 타입이어야 한다.

<!DOCTYPE html>
<html>
<body>
	<div id="root"></div>
</body>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
	function Btn({ what, fontSize = 16 }) {
		return <button
			style={{
				backgroundColor: "pink",
				color: "white",
				padding: "10px 20px",
				border: 0,
				borderRadius: 10,
				fontSize: fontSize
			}}
		>
			{what}
		</button>;
	}
	Btn.propTypes = {
		what: PropTypes.string,
		fontSize: PropTypes.number
	};
	function App() {
		return (
			<div>
				<Btn what="Save Changes" fontSize={18} />
				<Btn what="Continue" fontSize={"sixteen"} />
			</div>
		);
	}
	const root = document.getElementByID("root");
	ReactDOM.render(<App />, root);
</script>
</html>

0개의 댓글