본문 바로가기

React/공식문서(2023)

[리액트 공식문서 2023] Adding Interactivity

0. 학습 목표

In React, data that changes over time is called state.(시간이 지남에 따라 변하는 데이터를 state라 부른다)

In this chapter, you'll learn how to write components that handle interactions, update their state, and display different output over time.

1. Responding to events (subtitle: How to handle user-initiated events)

- React lets you add event handlers to you JSX. 

- Built-in components like <button> only support built-in browser events like onClick. However, you can also create your own components, and give their event handler props any application-specific names that you like. (<button>과 같은 빌트인 컴포넌트는 onClick 처럼 빌트인 브라우저 이벤트만 지원한다. 하지만, 자신만의 컴포넌트를 만들고 해당 이벤트 핸들러 props에 원하는 애플리케이션별 이름을 지정할 수도 있다.)

 

export default function App() {
  return (
    <Toolbar
      onPlayMovie={() => alert("Playing!")}
      onUploadImage={() => alert("Uploading!")}
    />
  );
}

function Toolbar({ onPlayMovie, onUploadImage }) {
  return (
    <div>
      <Button onClick={onPlayMovie}>
        Play Movie
      </Button>
      <Button onClick={onUploadImage}>
        Upload Image
      </Button>
    </div>
  );
}

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

학습 목표

- Different ways to write an event handler

- How to pass event handling logic from a parent component

- How events propagate and how to stop them

Adding Event Handlers

To add an event handler, you will first define a function and then pass it as a prop to the appropriate JSX tag.

 

You can make it show a message when a user clicks by following these three steps:

  1. Declare a function called handleClick inside your Button component.
  2. Implement the logic inside that function (use alert to show the message).
  3. Add onClick={handleClick} to the <button> JSX.
export default function Button() {
  function handleClick() {
    alert("You Clicked Me!");
  }
  
  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

 

handleClick is an event handler. Event handler functions:

  • Are usually defined inside your components.
  • Have names that start with handle, followed by the name of the event.

Reading Props in envent handlers(이벤트 핸들러에서 props 읽기)

Event handlers are declared inside of a component, they have access to the component's props.

 

function AlertButton({ message, children }) {
  return (
    <button onClick={() => alert(message)}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <AlertButton message="Playing">Play Movie</AlertButton>
      <AlertButton message="Uploading">Upload Image</AlertButton>
    </div>
  );
}

 

Passing Event Handlers as props(이벤트 핸들러를 props로 전달하기)

Often you'll want the parent component to specify a child's event handler. To do this, pass a prop the component recevies from its parents as the event handler like so. (컴포넌트가 부모로부터 받은 prop을 다음과 같이 이벤트 핸들러로 전달합니다)

 

function Button({ onClick, children }) {
  return (
    <button onClick={onClick}>
      {children}
    </button>
  );
}

function PlayButton({ movieName }) {
  function handlePlayClick() {
    alert(`Playing ${movieName}`);
  }
  
  return (
    <Button onClick={handlePlayClick}>
      Play, {movieName}!
    </Button>
  );
}

function UploadButton() {
  return (
    <Button onClick={() => alert("Uploading!!!")}>
      Upload Image
    </Button>
  );
}

export default function Toolbar() {
  return (
    <div>
      <PlayButton movieName="KiKi's Delivery Service"/>
      <UploadButton />
    </div>
  );
}

 

Naming Event Handler props (이벤트 핸들러 props 이름 정하기)

When you're building your own components, you can name thier event handler props any way that you like.

 

function Button({ onSmash, children }) {
  return (
    <button onClick={onSmash}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <Button onSmash={() => alert("Playing")}>
        Play Movie
      </Button>
      <Button onSmash={() => alert("Uploading")}>
        Upload Image
      </Button>
    </div>
  );
}

Event Propagation

Event handlers will also catch events from any children your component might have.

 

Stopping Propagation

Event handlers receive an event object as their only argument. By convention, it’s usually called e, which stands for “event”. You can use this object to read information about the event.

That event object also lets you stop the propagation. If you want to prevent an event from reaching parent components, you need to call e.stopPropagation() like this Button component does.

 

Preventing default behavior

Some browser events have default behavior associated with them. For example, a <form> submit event, which happens when a button inside of it is clicked, will reload the whole page by default.

You can call e.preventDefault() on the event object to stop this from happening.

 

🚨 Notice

📚 Summary

  • You can handle events by passing a function as a prop to an element like <button>.
  • Event handlers must be passed, not called! onClick={handleClick}, not onClick={handleClick()}.
  • You can define an event handler function separately or inline.
  • Event handlers are defined inside a component, so they can access props.
  • You can declare an event handler in a parent and pass it as a prop to a child.
  • You can define your own event handler props with application-specific names.
  • Events propagate upwards. Call e.stopPropagation() on the first argument to prevent that. (이벤트는 위쪽으로 전파됩니다. 이를 방지하려면 e.stopPropagation()을 호출하세요)
  • Events may have unwanted default browser behavior. Call e.preventDefault() to prevent that.
  • Explicitly calling an event handler prop from a child handler is a good alternative to propagation. (자식 핸들러에서 이벤트 핸들러 prop을 명시적으로 호출하는 것은 전파에 대한 좋은 대안입니다)

2. State: a component's memory (subtitle: How to make components "remember" information with state)

Components often need to change what’s on the screen as a result of an interaction. Typing into the form should update the input field, clicking “next” on an image carousel should change which image is displayed, clicking “buy” should put a product in the shopping cart. Components need to “remember” things: the current input value, the current image, the shopping cart. In React, this kind of component-specific memory is called state.(React에서는 이런 종류의 컴포넌트별 메모리를 state라고 부릅니다)

학습 목표

- How to add a state variable with useState Hook (useState 훅으로 state 변수를 추가하는 방법)

- What pair of values the useState Hook returns (useState 훅이 반환하는 값 쌍)

- How to add more than one state variable (state 변수를 두 개 이상 추가하는 방법)

- Why state is called local (state를 지역적이라고 하는 이유)

When a regular variable isn't enough

import { sculptureList } from './data.js';

export default function Gallery() {
  let index = 0;

  function handleClick() {
    index = index + 1;
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i> 
        by {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

 

  1. Local variables don’t persist between renders. When React renders this component a second time, it renders it from scratch—it doesn’t consider any changes to the local variables.
  2. Changes to local variables won’t trigger renders. React doesn’t realize it needs to render the component again with the new data.
  1. 지역 변수는 렌더링 간에 유지되지 않습니다. React는 이 컴포넌트를 두 번째로 렌더링할 때 지역 변수에 대한 변경 사항을 고려하지 않고 처음부터 렌더링합니다.
  2. 지역 변수를 변경해도 렌더링을 발동시키지 않습니다. React는 새로운 데이터로 컴포넌트를 다시 렌더링해야 한다는 것을 인식하지 못합니다.

To update a component with new data, two things need to happen: 컴포넌트를 새 데이터로 업데이트하려면 두 가지 작업이 필요합니다:

  1. Retain the data between renders.
  2. Trigger React to render the component with new data (re-rendering).
  1. 렌더링 사이에 데이터를 유지합니다.
  2. 새로운 데이터로 컴포넌트를 렌더링(리렌더링)하도록 React를 촉발 합니다.

The useState Hook provides those two things: useState 훅은 이 두 가지를 제공합니다:

  1. A state variable to retain the data between renders.
  2. A state setter function to update the variable and trigger React to render the component again.
  1. 렌더링 사이에 데이터를 유지하기 위한 state 변수.
  2. 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 촉발하는 state 설정자 함수.

Adding a state variable

 

Giving a component multiple state variables

State is isolated and private

 

 

3. Rnder and commit (subtitle: How React updates the UI in two phases)

 

 

4. State as a snapshot (subtitle: Why state doesn't update right after you change it)

- state 변수는 읽고 쓸 수 있는 일반 JavaScript 변수처럼 보일 수 있습니다.

- 하지만 state는 스냅샷처럼 동작합니다.

- state 변수를 설정해도 이미 가지고 있는 state 변수는 변경되지 않고, 대신 리렌더링이 실행됩니다.

학습 목

- How setting state triggers re-renders (state 설정으로 리렌더링이 촉발되는 방식)

- When and how state updates (state 업데이트 시기 및 방법)

- Why state does not update immediately after you set it (state를 설정한한 직후에 state가 업데이트 되지 않는 이유)

- How event handlers access a "snapshot" of the state (이벤트 핸들러가 state의 '스냅샷'에 액세스하는 방법)

Setting state triggers renders (state를 설정하면 렌더링이 촉발됩니다)

you saw that setting state requests a re-render from React.

This means that for an interface to react to the event, you need to update the state.

state를 설정하면 React에 리렌더링을 요청하는 것을 보았습니다.

즉, 인터페이스가 이벤트에 반응하려면 state를 업데이트해야 합니다.

 

import { useState } from 'react';

export default function Form() {
  const [isSent, setIsSent] = useState(false);
  const [message, setMessage] = useState('Hi!');
  if (isSent) {
    return <h1>Your message is on its way!</h1>
  }
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      setIsSent(true);
      sendMessage(message);
    }}>
      <textarea
        placeholder="Message"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Send</button>
    </form>
  );
}

function sendMessage(message) {
  // ...
}

 

  1. The onSubmit event handler executes.
  2. setIsSent(true) sets isSent to true and queues a new render.
  3. React re-renders the component according to the new isSent value.

Rendering takes a snapshot in time (렌더링은 그 시점의 스냅샷을 찍습니다)

“Rendering” means that React is calling your component, which is a function. The JSX you return from that function is like a snapshot of the UI in time. Its props, event handlers, and local variables were all calculated using its state at the time of the render.

“렌더링”이란 React가 컴포넌트, 즉,함수를 호출한다는 뜻입니다. 해당 함수에서 반환하는 JSX는 시간상 UI의 스냅샷과 같습니다. prop, 이벤트 핸들러, 로컬 변수는 모두 렌더링 당시의 state를 사용해 계산됩니다.

 

When React re-renders a component: React가 컴포넌트를 다시 렌더링할 때:

  1. React calls your function again. (React가 함수를 다시 호출합니다)
  2. Your function returns a new JSX snapshot. (함수가 새로운 JSX 스냅샷을 반환합니다)
  3. React then updates the screen to match the snapshot you’ve returned. (그러면 React가 반환한 스냅샷과 일치하도록 화면을 업데이트합니다)

As a component’s memory, state is not like a regular variable that disappears after your function returns. State actually “lives” in React itself—as if on a shelf!—outside of your function. When React calls your component, it gives you a snapshot of the state for that particular render. Your component returns a snapshot of the UI with a fresh set of props and event handlers in its JSX, all calculated using the state values from that render!

- 컴포넌트의 메모리로서 state는 함수가 반환된 후 사라지는 일반 변수와 다릅니다.

- state는 실제로 함수 외부에 마치 선반에 있는 것처럼 React 자체에 “존재”합니다.

- React가 컴포넌트를 호출하면 특정 렌더링에 대한 state의 스냅샷을 제공합니다.

- 컴포넌트는 해당 렌더링의 state 값을 사용해 계산된 새로운 props 세트와 이벤트 핸들러가 포함된 UI의 스냅샷을 JSX에 반환합니다!

 

 

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}

 

state를 설정하면 다음 렌더링에 대해서만 변경됩니다.

첫 번째 렌더링에서 number0이었습니다. 따라서 해당 렌더링의 onClick 핸들러에서 setNumber(number + 1)가 호출된 후에도 number의 값은 여전히 0입니다.

setNumber(number + 1)를 세 번 호출했지만, 이 렌더링에서 이벤트 핸들러의 number는 항상 0이므로 state를 1로 세 번 설정했습니다.

이것이 이벤트 핸들러가 완료된 후 React가 컴포넌트안의 number3이 아닌 1로 다시 렌더링하는 이유입니다.

State over time (시간 경과에 따른 state)

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);
        alert(number);
      }}>+5</button>
    </>
  )
}

 

A state variable’s value never changes within a render, even if its event handler’s code is asynchronous. Inside that render’s onClick, the value of number continues to be 0 even after setNumber(number + 5) was called. Its value was “fixed” when React “took the snapshot” of the UI by calling your component.

- state 변수의 값은 이벤트 핸들러의 코드가 비동기적이더라도 렌더링 내에서 절대 변경되지 않습니다. 해당 렌더링의 onClick 내에서, setNumber(number + 5)가 호출된 후에도 number의 값은 계속 0입니다. 이 값은 컴포넌트를 호출해 React가 UI의 “스냅샷을 찍을” 때 “고정”된 값입니다.

 

React keeps the state values “fixed” within one render’s event handlers. 

React는 하나의 렌더링 이벤트 핸들러 내에서 state 값을 “고정”으로 유지합니다.

 

📚 Summary

  • Setting state requests a new render. (state를 설정하면 새 렌더링을 요청합니다)
  • React stores state outside of your component, as if on a shelf. (React는 컴포넌트 외부에 마치 선반에 보관하듯 state를 저장합니다)
  • When you call useState, React gives you a snapshot of the state for that render. (‘useState’를 호출하면 React는 해당 렌더링에 대한 state의 스냅샷을 제공합니다)
  • Variables and event handlers don’t “survive” re-renders. Every render has its own event handlers.(변수와 이벤트 핸들러는 다시 렌더링해도 “살아남지” 않습니다. 모든 렌더링에는 자체 이벤트 핸들러가 있습니다)
  • Every render (and functions inside it) will always “see” the snapshot of the state that React gave to that render. (모든 렌더링(과 그 안에 있는 함수)은 항상 React가 그 렌더링에 제공한 state의 스냅샷을 “보게” 됩니다)
  • You can mentally substitute state in event handlers, similarly to how you think about the rendered JSX. (렌더링된 JSX에 대해 생각하는 것처럼 이벤트 핸들러에서 state를 정신적으로 대체할 수 있습니다)
  • Event handlers created in the past have the state values from the render in which they were created. (과거에 생성된 이벤트 핸들러는 그것이 생성된 렌더링 시점의 state 값을 갖습니다)

5. Queueing a series of state updates (subtitle: How to queue multiple state updates)

Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how React batches state updates

- state 변수를 설정하면 다음 렌더링이 큐(대기열, queue)에 들어갑니다.

- 그러나 경우에 따라 다음 렌더링을 큐에 넣기 전에, 값에 대해 여러 작업을 수행하고 싶을 때도 있습니다. 이를 위해서는 React가 state 업데이트를 어떻게 배치하면 좋을지 이해하는 것이 도움이 됩니다.

학습 목표

- What “batching” is and how React uses it to process multiple state updates (일괄처리(배칭, batching)이란 무엇이며 React가 여러 state 업데이트를 처리하는 방법) 

- How to apply several updates to the same state variable in a row (동일한 state 변수에서 여러 업데이트를 적용하는 방법)

React batches state updates

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 1);
        setNumber(number + 1);
        setNumber(number + 1);
      }}>+3</button>
    </>
  )
}

 

- setNumber(number + 1) 를 세 번 호출하므로 “+3” 버튼을 클릭하면 세 번 증가할 것으로 예상할 수 있습니다

- 각 렌더링의 state 값은 고정되어 있으므로, 첫번째 렌더링의 이벤트 핸들러의 numbersetNumber(1)을 몇 번 호출하든 항상 0입니다.

- React waits until all code in the event handlers has run before processing your state updates.(React는 state 업데이트를 하기 전에 이벤트 핸들러의 모든 코드가 실행될 때까지 기다립니다)

- 리렌더링은 모든 setNumber() 호출이 완료된 이후에만 일어납니다.

Updating the same state multiple times before the next render

- 다음 렌더링 전에 동일한 state 변수를 여러 번 업데이트 하고 싶다면 setNumber(number + 1) 와 같은 다음 state 값을 전달하는 대신, setNumber(n => n + 1) 와 같이 큐의 이전 state를 기반으로 다음 state를 계산하는 함수를 전달할 수 있습니다. (흔하지는 않음)

- 이는 단순히 state 값을 대체하는 것이 아니라 React에게 “state 값으로 무언가를 하라”고 지시하는 방법입니다.

- 여기서 n => n + 1 는 **업데이터 함수(updater function)**라고 부릅니다

 

import { useState } from 'react';

export default function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  )
}

 

  1. React는 이벤트 핸들러의 다른 코드가 모두 실행된 후에 이 함수가 처리되도록 큐에 넣습니다.
  2. 다음 렌더링 중에 React는 큐를 순회하여 최종 업데이트된 state를 제공합니다.

 

After the event handler completes, React will trigger a re-render. During the re-render, React will process the queue. Updater functions run during rendering, so updater functions must be pure and only return the result. Don’t try to set state from inside of them or run other side effects. In Strict Mode, React will run each updater function twice (but discard the second result) to help you find mistakes.

- 이벤트 핸들러가 완료되면 React는 리렌더링을 실행합니다.

- 리렌더링하는 동안 React는 큐를 처리합니다.

- 업데이터 함수는 렌더링 중에 실행되므로, 업데이터 함수는 순수해야 하며 결과만 반환해야 합니다. 업데이터 함수 내부에서 state를 변경하거나 다른 사이드 이팩트를 실행하려고 하지 마세요. Strict 모드에서 React는 각 업데이터 함수를 두 번 실행(두 번째 결과는 버림)하여 실수를 찾을 수 있도록 도와줍니다.

 

📚 Summary

  • Setting state does not change the variable in the existing render, but it requests a new render. (state를 설정하더라도 기존 렌더링의 변수는 변경되지 않으며, 대신 새로운 렌더링을 요청합니다)
  • React processes state updates after event handlers have finished running. This is called batching. (React는 이벤트 핸들러가 실행을 마친 후 state 업데이트를 처리합니다. 이를 일괄처리(배칭, batching)라고 합니다)
  • To update some state multiple times in one event, you can use setNumber(n => n + 1) updater function. (하나의 이벤트에서 일부 state를 여러 번 업데이트하려면 setNumber(n => n + 1) 업데이터 함수를 사용할 수 있습니다)

6. Updating objects in state (subtitle: How to update an object in state)

 

7. Updating arrays in state (subtitle: How to update an array in state)

 

'React > 공식문서(2023)' 카테고리의 다른 글

[리액트 공식문서 2023] Describing the UI  (0) 2023.11.01