Evnet
how to add event listener?
바닐라 js에서 어플리케이션을 dynamic하게 만드는 가장 흔한 방법은 event listener를 등록하는 것이다.
// Imperative Vanila JS
const deleteBtn = document.querySelector('.delete');
deleteBtn.addEventListener('click', ()=>{
console.log('you clicked delete btn');
})
그러나 리액트는 바닐라 자바스크립트의 명령형이 아니라, 선언형(declarative) 접근방식을 이용해서 코드를 작성한다. 그래서 이벤트를 등록할 때 위의 예제처럼 직접 DOM 요소를 조작하는 것이 아니라, 그 이벤트를 컴포넌트의 property로 전달한다.
const DeleteBtn = () => {
const clickHandler = ()=> {
console.log('clicked');
}
return (
<button type='button' onClick={clickHandler}>
delete
</button>
);
};
- 등록할 수 있는 모든 이벤트 property의 name은 on으로 시작한다.
- 이벤트 property의 value는 그 이벤트가 발생했을 때 실행할 콜백 함수가 된다.
- conventional namimg callback function | event + handler
- 일반적으로 이벤트 property를 custom component에 전달할 수 없다(코드로 명시하면 이벤트 등록 가능).
form event
form | onSubmit
form이 제출되면(onSubmit 이벤트가 발생하면), 브라우저는 request를 action 속성에 등록된 서버에 전달하며 page를 refresh한다. 이러한 브라우저의 기본 기능을 제어하고 싶을 때에는 아래와 같이 코드를 작성한다.
const NewExpenseForm = () => {
const submitHandler = (e) => {
e.preventDefault();
};
const clickHandler = () => {
};
return (
<form action='#' onSubmit={submitHandler}>
<!-- ... -->
<button type='submit' onClick={clickHandler}>Add</button>
</form>
);
};
input | onChange
const NewExpenseForm = () => {
const changeHandler = (e) => {
console.log(e.target.value);
};
return (
<form className='expense-form' action='#'>
<label htmlFor='title'>Title</label>
<input
type='text'
name='title'
id='title'
onChange={changeHandler}
/>
<button type='submit'>Add</button>
</form>
);
};
- onChange | 모든 input type에 등록할 수 있으며, input의 value에 변화가 생겼을 때 이벤트가 발생한다.
- vanila js에서 처럼, 이벤트가 발생하면 자동으로 이벤트 객체가 생성된다.
- event.target.value
- input에 전달된 값을 저장하고, 그 값을 렌더링해서 보여주어야 할 때에는 useState를 이용할 수 있다.
State
why we need state?
const Description = (props) => {
let title= props.title;
const clickHandler = () => {
title = 'updated!';
};
return (
<section className='item-description'>
<h1 className='description-title' onClick={clickHandler}>{title}</h1>
<p className='description-price'>{props.price}</p>
</section>
);
};
- 클릭 이벤트가 발생하면, clickHandler가 실행된다. => title 변수의 값이 'updated'로 바뀐다.
- 그러나 위의 예제코드는 예상한 대로 동작하지 않는다. => 화면에 'updated'가 출력되지 않는다.
- why?
- component는 JSX를 반환하는 함수이다.
- component 함수를 실행하는 것은 우리가 아니라, 리액트이다.
- 리액트는 더이상 반환할 JSX 코드가 없을 때 까지 모든 component 함수를 실행한다.
- app이 시작될 때 index.js가 가장 먼저 실행된다.
- 이 때 index의 <App /> 컴포넌트 함수를 실행하고, <App /> 컴포넌트의 자식 컴포넌트를 실행하고 ... (반복)
- JSX 코드가 DOM element로 변환되어 브라우저에 렌더링된다.
- 리액트는 어플리케이션이 처음 렌더링 될 때에만 컴포넌트 함수를 실행한다. 즉, 컴포넌트 함수가 반복적으로 실행되지 않기 때문에 변화된 상태가 화면에 렌더링되지 않는 것이다.
- state는 컴포넌트 함수를 재실행함으로써 UI를 업데이트하기 위해 필요한 개념이다.
how to use state? | useState
import { useState } from 'react';
const Description = (props) => {
const [title, setTitle] = useState(props.title);
const clickHandler = () => {
setTitle('updated');
console.log(title); // 처음 이벤트가 발생했을 때에는 updated가 아니라 초기값이 출력된다.
};
return (
<section className='item-description'>
<h1 className='description-title' onClick={clickHandler}>{title}</h1>
<p className='description-price'>{props.price}</p>
</section>
);
};
- re-rendering
- 리액트는 컴포넌트 내부에서 등록한 이벤트가 발생하거나, 선언된 변수의 값이 변경된다고 해서 그 컴포넌트 함수를 다시 실행하지 않는다.
- 대신 useState를 실행함으로써, state로 관리하는 특별한 변수를 만들 수 있다. 이 값이 변경되면 컴포넌트 함수를 재실행한다.
- 컴포넌트 함수가 재실행된다는 것은, 그 컴포넌트를 다시 렌더링 할 수 있다는 것을 의미한다. 즉 state를 이용해서 component를 조작하고, 최종적으로 화면에 렌더링 되는 DOM을 조작할 수 있다.
- 이 때 useState(init value)도 다시 실행되기 때문에, set 함수를 이용해서 업데이트한 state 값이 덮어씌워질 것 같지만, 그런 일은 발생하지 않는다.
- 리액트가 하는 일은 state를 관리하고, 그 state와 컴포넌트를 연결해 주는 것이다.
- state를 관리하는 프로세스에서 컴포넌트 함수가 최초로 실행될 때에만 초기값을 고려하기 때문에, 초기 값이 계속 덮어 씌워지는 일은 발생하지 않는다.
- 단, 컴포넌트가 dom에서 제거되었다가 다시 나타나는 경우는 컴포넌트 함수가 최초로 실행되는 때로 간주한다.
- const [title, setTitle] = useState(props.title)
- useState 함수는 배열을 반환한다.
- 배열의 첫번째 아이템은 인자로 전달받은 값을 초기값으로 갖는 특별한 변수가 된다.
- 배열의 두번째 아이템은 해당 변수의 값을 업데이트하고, 값이 업데이트 되었을 때 re-rendering이 일어나도록 지시하는 함수이다.
- 배열의 첫번째, 두번째 아이템을 array destructuring을 이용해서 변수에 할당한다.
- useState 함수는 컴포넌트 함수의 바로 안쪽에서 호출해야 한다(must call directly inside of the function).
- 함수 밖에서 호출하거나, 중첩된 컨텍스트에서 호출하면 안됨.
- ??? 예외가 있긴 있음
- setTitle
- set 함수는 단순히 값을 재할당하는 것이 아니라, 값을 업데이트하고 컴포넌트 함수의 재실행(re-rendering)을 지시하는 함수이다. 따라서 title이라는 특별한 변수가 만들어지긴 했지만, title에 값을 재할당하는 것이 아니라 set 함수를 이용해서 값을 업데이트해야 한다.
- title 변수는 const로 선언되었기 때문에, 어차피 등호(=)를 이용해서 값을 재할당하는 것은 불가능하다.
- set함수가 title의 값을 업데이트 하는 방식도 등호를 이용하는 것은 아니다.
- 이 때 set 함수는 바로 값을 업데이트 하는 것이 아니라, 업데이트를 예약하는 함수이다(scheduling). => console에 업데이트한 값이 바로 출력되지 않는다.
- set 함수는 단순히 값을 재할당하는 것이 아니라, 값을 업데이트하고 컴포넌트 함수의 재실행(re-rendering)을 지시하는 함수이다. 따라서 title이라는 특별한 변수가 만들어지긴 했지만, title에 값을 재할당하는 것이 아니라 set 함수를 이용해서 값을 업데이트해야 한다.
- instance based state | 각 컴포넌트는 자신만의 독립적인 state를 갖는다.
- 어떤 컴포넌트가 여러 개 재사용 된다고 하더라도, 각 컴포넌트의 state는 독립적으로 관리된다.
- 예를 들어, 어떤 Description 컴포넌트의 title을 클릭한다고 해서, 다른 모든 Description 컴포넌트의 title이 함께 업데이트되지는 않는다. => 컴포넌트 함수가 실행(렌더링)될 때 마다 console에 메세지를 출력하게 만들면, 이를 가시적으로 확인해 볼 수 있다.
+) useState 함수는 React Hook의 하나이다.
+) props와 state의 차이점 | state는 컴포넌트 안에서 관리되는 data이고, props는 컴포넌트에 전달되는 data이다. props는 부모 컴포넌트의 state를 받을 수도 있다. 이 때 state를 직접 수정하는 것은 지양한다(shallow comparison 때문).
scheduling
set 함수를 이용해서 state의 값을 업데이트 할 수 있다. 이 때 set 함수 자체가 state를 업데이트 하는것이 아니라 다음의 단계를 거친다.
- set 함수를 호출하면,
- set 함수는 state 업데이트 함수를 호출하고, (schedule a state update)
- 이 함수가 state를 업데이트한다.
stateful vs. stateless
- stateful component | state를 가지고 있는 컴포넌트
- stateless component | props을 통해서 data를 받아오고, 단지 data를 출력하기만 하는 컴포넌트. presentational component라고 부르기도 한다.
거의 모든 어플리케이션에서 stateless 컴포넌트가 stateful 컴포넌트보다 많다.
'Study > React' 카테고리의 다른 글
Dynamic List (0) | 2022.06.17 |
---|---|
prevState (0) | 2022.06.16 |
JSX (0) | 2022.06.16 |
Component & Props (0) | 2022.06.16 |
What is React? (0) | 2022.06.16 |