Now Loading ...
-
리액트 시작하기 (6 ~ 장)
State
리액트 Component의 상태를 관리하는 객체
state는 개발자가 정의한다.
랜더링이나 데이터의 흐름에 사용되는 값들만 state에 포함시켜야한다.
state가 변경될 경우, 컴포넌트가 재 랜더링 되기 때문에 랜더링과 데이터의 흐름에 관계없는 데이터를 저장하면, 컴포넌트가 재 랜더링되어 성능에 영향을 줄 수 있다.
컴포넌트의 인스턴스 필드로 정의
Javascript 객체라 생각
class LikeButton extends React.Component {
constructor(props) {
super(props);
this.state = {liked: false}; // state 정의
}
}
state는 직접 수정할 수 없다.
(X)
this.state = {name : 'Inje'}
this.setState({
name: 'Inje'
});
컴포넌트가 계속 존재하는게 아니라 시간에 따라 생성, 업데이트되다가 사라진다.
Hooks
함수 컨퍼넌트도 class 컴포넌트처럼 state를 가질 수 있게 해주는 기능
갈고리
함수명 앞에 use가 붙어서 훅이라는 것을 명시해준다.
useState Hook
state 를 사용하기 위한 Hook
const [변수명, set함수명] = useState(초기값);
// return 값은 배열 -> 1. state 변수, 2. state를 업데이트하는(set) 함수
import React, {useState} from "react";
function Counter(props) {
const [count, setCount] = useState(0); // count라는 state 변수와 setCount라는 업데이트 함수 생성
// setCount -> 변수 각가에 대해 set함수가 따로 존재!
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
useEffect()
side effect를 수행하기 위한 Hook
side effect = 효과, 영향
다른 컴포넌트에 영향을 미칠 수 있으며, 렌더링 중에는 작업이 완료될 수 없기 때문이다.
함수 컴포넌트에서 Side effect를 실행할 수 있게 해주는 Hook
useEffect(이펙트 함수, 의존성 배열);
첫번째 컴포넌트가 랜더링 된 이후, 재 랜더링 된 이후에 실행됨
useEffect(이펙트 함수);
의존성 배열 생략 시, 컴포넌트가 업데이트 될 때마다 호출됨
useEffect와 useState를 함께 사용하여 컴포넌트의 상태를 관리할 수 있다.
import React, {useState, useEffect} from "react";
function Counter(props) {
const [count, setCount] = useState(0);
// componentDidMount, componentDidUpdate와 비슷하게 작동한다
useEffect(() => {
// 브라우저 API를 사용해서 document의 title을 업데이트
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>총 {count}번 클릭했습니다.</p>
<button onClick={() => setCount(count + 1)}>
클릭
</button>
</div>
);
}
import React, {useState, useEffect} from "react";
function UserStatus(pros) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
seInOnline(status.isOnline);
}
useEffect( () => {
ServerAPI.subscribeUserStatus(props.userId, handleStatusChange);
return () => {
ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
};
});
if(isOnline === null) {
return '대기 중...';
}
return isOnline ? '온라인' : '오프라인';
}
function UserStatusWithCounter(props) {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `총 ${count}번 클릭했습니다.`;
});
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
ServerAPI.subscribeUserStatus(props.userId, handleStatusChange);)
return () => {
ServerAPI.unsubscribeUserStatus(props.userId, handleStatusChange);
};
});
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
}
useMemo, useCallback, useRef
useMemo
Memoized value를 리턴하는 Hook
Memoization : 최적화를 사용하는 개념, 연산 값이 많은 값을 저장해놨다가 이전에 사용한 결과를 받아옴
사용법
const memoizedValue = useMemo( () => {
// 연산값이 높은 작업을 수행하여 결과를 반환
return computeExpensiveValue(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
랜더링에서 일어날 내용을 넣으면 안됨 ex) useEffect에서 실행되어야 할 side effect
의존성 배열을 넣지 않으면 컴포넌트가 랜더링 될 때마다 실행된다.
const memoizedValue = useMemo(
() => computeExpensiveValue(a,b)
);
의존성배열이 빈 배열인 경우, 컴포넌트 mount 시에만 호출된다. -> mount 이후에는 변경되지 않음
const memoizedValue = useMemo(
() => computeExpensiveValue(a,b),
[]
);
useMemoHook에 의존성 변수를 넣고, 해당 변수의 값이 변해야 할 경우에만 사용한다.
useCallback()
값이 아닌 함수를 반환
const memoizedCallback = useCallback(
() => {
doSomething(의존성 변수1, 의존성 변수2);
},
[의존성 변수1, 의존성 변수2]
);
따라서 useCallback 과 useMemo 의 함수 선언은 같은 말이다.
useCallback(함수, 의존성 배열); // 위아래 둘다 같은 내용
useMemo(() => 함수, 의존성 배열); // 위아래 둘다 같은 내용
useRef()
reference를 사용하기 위한 Hook -> 특정 컴포넌트를 접근할 수 있는 객체
refObject.current 속성 -> ref 하는 엘리먼트
const refContainer = useRef(null);
컴포넌트가 mount 되기 전까지 계속 유지된다.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
function onButtonClick() {
// `current`가 가리키는 DOM 노드에 포커스를 맞춘다.
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>
Focus the input
</button>
</>
);
}
내부의 데이터가 변경되었을 때, 별도로 알려주지 않는다.
useCallback은 자식 엘리먼트가 변경되었을 때, 알림을 받을 수 있다.
Callback Ref
const mesureRef = useCallback(node => {
if(node !== null) {
// node가 변경되었을 때, 알림을 받는다.
console.log('엘리먼트가 변경되었습니다.', node);
}
}, []);
Hook의 규칙과 Custom Hook 만들기
Hook은 컴포넌트의 최상위 레벨에서만 호출해야 한다.
Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.
잘못된 Hook 사용법
function MyComponent(props) {
const [name, setName] = useState('Inje');
if(name !== ''){
useEffect( () => {
... // 이 부분은 잘못된 사용 예시입니다.
});
}
}
리액트 함수 컴포넌트에서만 Hook을 호출해야 한다.
일반적인 javascript에서 호출 X
eslint-plugin-react-hooks를 사용하여 Hook의 규칙을 검사할 수 있다.
Custom Hook
Hook을 재사용할 수 있는 방법
Custom Hook 추출하기
이름이 use 로 시작하고, 내부에서 다른 Hook을 호출하는 하나의 자바스크립트 함수
```jsx
// useUserStatus 를 사용하여 사용자 상태를 관리하는 Custom Hook
function UserStatus(props) {
const isOnline = useUserStatus(props.user.id);
if(isOnline === null){
return ‘대기 중…’;
}
return isOnline ? ‘온라인’ : ‘오프라인’;
}
function UserListItem(props) {
const isOnline = useUserStatus(props.user.id);
return (
<li>
{props.user.name} - {isOnline ? '온라인' : '오프라인'}
</li>
); } ``` - 여러개의 컴포넌트에서 하나의 Custom Hook을 사용할 때, 컴포넌트 내부에 있는 모든 state와 effect는 전부 분리되어 있다. - Custom Hook은 컴포넌트의 상태를 공유하지 않는다. (분리된 state와 effect를 가진다.) - 분리된 데이터들을 공유하고싶을 때, ```jsx function CheckUserStatus(props) {
/ ** Custom Hook을 사용하여 사용자 상태를 관리하는 예시
const [userId, setUserId] = useState(1);
const isUserOnline = useUserStatus(userId);
/ ** 으로 하면, 이전에 선택된 사용자를 취소하고, 새로운 사람의 상태를 다시 구독한다. } ```
-
리액트 시작하기 (1 ~ 5장)
리액트 설치 방법
npm 설치
node 설치
툴은 원하는대로
JSX 문법
자바스크립트 확장 문법
JavaScript + XML/HTML
ex) 예시
const element = <h1>Hello, world!</h1>;
원리
React.createElement(
type,
[props], ## 속성
[...children] ## 자식 엘리먼트
)
JSX 문법의 장점
가독성 향상: HTML과 유사한 문법으로 작성되어, 코드가 더 직관적이고 이해하기 쉬움
Injection 방지: JSX는 자바스크립트 표현식을 안전하게 HTML에 삽입할 수 있도록 하여, XSS 공격을 방지하는 데 도움을 줌
{} 가 들어가면 무조건 자바스크립트 코드로 바뀜
Rendering Element
Elements의 정의와 생김새
어떤 물체를 구성하는 성분
리액트 앱을 구성하는 가장 작은 블록들
화면에 보이는 것들을 기술
리액트(Elements)는 자바스크립트 객체 형태로 존재
Elements의 특징 및 렌더링하기
불변성 (immutable)
변경된 부분을 계산하여 해당 부분만 다시 렌더링
Root dom node
ReactElement : React 의 Virtual DOM에 존재
DOMElement : 실제 브라우저의 DOM에 존재
ReactDOM.render()를 통해 ReactElement를 DOMElement로 변환
Components 와 Props
컴포넌트의 정의
컴포넌트는 UI를 구성하는 독립적인 재사용 가능한 코드 블록
컴포넌트는 함수나 클래스 형태로 정의되며, 입력값(Props)을 받아 UI를 렌더링
컴포넌트는 상태(State)를 가질 수 있으며, 상태가 변경되면 UI가 자동으로 업데이트됨
컴포넌트는 계층 구조로 구성되어, 상위 컴포넌트가 하위 컴포넌트를 포함할 수 있음
컴포넌트는 Props를 통해 데이터를 전달받아 렌더링
컴포넌트는 재사용 가능하고, 유지보수가 용이함
Props의 정의
Props는 컴포넌트에 전달되는 입력값 (React의 속성)
ReadOnly 속성으로, 컴포넌트 내부에서 변경할 수 없음
모든 리액트 컴포넌트들은 그들의 Props에 관해서는 Pure(순수)함수 같은 역할을 해야한다.
Pure : 입력값이 동일하면 항상 동일한 출력을 반환하는 함수
모든 리액트 컴포넌트는 Props를 통해 데이터를 전달받아 렌더링해야한다.
function App(pros){
return (
<Profile
name="John Doe"
age= {30}
/>
);
}
function App(pros){
return (
<Layout
width={100}
height={200}
header={<Header title="제목"/>}
footer={<Footer/>}
/>
);
}
Props의 특징 사용법
Props는 컴포넌트에 전달되는 데이터로, 컴포넌트의 동작과 렌더링을 제어
function App(props) {
return (
<Profile
name="소플"
introduction="안녕하세요, 소플입니다."
viewCount={1000}
/>
);
}
Component 만들기 및 랜더링
클래스 컴포넌트, 함수 컴포넌트
주로 함수 컴포넌트를 사용한다함
그래도 클래스 컴포넌트를 알고있어야 한다.
함수 컴포넌트
react Component를 일종의 함수로 생각한다.
function Welcome(props) {
return <h1>안녕하세요, {props.name}님!</h1>;
}
클래스 컴포넌트
javascript es6 문법을 사용하여 만든 컴포넌트
class Welcome extends React.Component {
render() {
return <h1>안녕하세요, {this.props.name}님!</h1>;
}
}
컴포넌트의 이름
항상 대문자로 시작해야함 (소문자로 입력하면 DOM 엘리먼트로 인식됨)
const element = <Welcome name="소플" />;
컴포넌트 렌더링
function Welcome(props) {
return <h1>안녕하세요, {props.name}님!</h1>;
}
const element = <Welcome name="소플" />;
ReactDOM.render(
element,
document.getElementById('root')
);
컴포넌트 합성과 추출
컴포넌트 합성
복잡한 화면을 여러개의 Components로 나눠서 구현
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
컴포넌트 추출
큰 컴포넌트를 작은 컴포넌트로 나누어 재사용성을 높임
function Component(props) {
return (
<div className="content">
<div className="user-info">
<img className="avatar"
src={props.author.avatarUrl}
alt={props.author.name}
/>
<div className="user-info-name">
{props.author.name}
</div>
</div>
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
Avatar 추출
function Avatar(props) {
return (
<img className="avatar"
src={props.user.avatarUrl}
alt={props.user.name}
/>
);
}
function Component(props) {
return (
<div className="content">
<div className="user-info">
<Avatar user={props.author} />
<div className="user-info-name">
{props.author.name}
</div>
</div>
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
UserInfo 추출
function UserInfo(props) {
return (
<div className="user-info">
<Avatar user={props.user} />
<div className="user-info-name">
{props.user.name}
</div>
</div>
);
}
function Component(props) {
return (
<div className="content">
<UserInfo user={props.author} />
<div className="comment-text">
{props.text}
</div>
<div className="comment-date">
{formatDate(props.date)}
</div>
</div>
);
}
(실습) 댓글 컴포넌트 만들기
Comment.jsx
```jsx
import React from “react”;
function Comment(props) {
return (
<div>
<div>
</div>
<div>
{props.name}
{props.comment}
</div>
</div>
); }
export default Comment;
- CommentList.jsx
```jsx
import React from "react";
import Comment from "./Comment";
const comments = [
{ name: "test", comment: "안녕하세요 test." },
{ name: "test2", comment: "안녕하세요 test2." },
{ name: "test3", comment: "안녕하세요 test3." },
];
function CommentList(props) {
return (
<div>
{comments.map((comment) => {
return (
<Comment name={comment.name} comment={comment.comment}/>
)
})}
</div>
);
}
export default CommentList;
App.jsx
const root = createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<CommentList />
</React.StrictMode>,
document.getElementById('root')
);
Touch background to close