React
๋ W3C ๋ช
์ธ์ ๋ฐ๋ผ ํฉ์ฑ ์ด๋ฒคํธ๋ฅผ ์ ์ํ๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ํธํ์ฑ์ ๋ํด ๊ฑฑ์ ํ ํ์๊ฐ ์์ต๋๋ค.
ํฉ์ฑ ์ด๋ฒคํธ๋ Synthetic Event
๊ฐ์ฒด๋ก ์ ์๋๋ฉฐ ๋ธ๋ผ์ฐ์ ๊ณ ์ ์ด๋ฒคํธ์ ์์ ํ ๋์ผํ๊ฒ ๋์ํ์ง๋ ์์ต๋๋ค.
์ด๋ฒ ํฌ์คํธ์์๋ React ์ ๋ด๋ถ ์ด๋ฒคํธ ์์คํ ์ ๋์ ๋ฐฉ์์ ๋ํด์ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ๐
๐ SyntheticEvent ๋ํผ ๊ฐ์ฒด
React
์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ง์ ์ผํ์ผ๋ก
๋ชจ๋ ํ๋ซํผ์์ ๋์ผํ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ฅผ ์ํด ๊ตฌํ๋ ์ธํฐํ์ด์ค์ธSyntheticEvent
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ด๋ stopPropagation
๊ณผ preventDefault
๋ฑ์ ํฌํจํ์ฌ
๋ธ๋ผ์ฐ์ ๊ณ ์ ์ด๋ฒคํธ์ ๋์ผํ ์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ง์ง๋ง ๋ชจ๋ ๋ธ๋ผ์ฐ์ ์์ ๋์ผํ๊ฒ ๋์ํจ์ ๋ณด์ฅํฉ๋๋ค.
Synthetic Event ํ์
ํฉ์ฑ ์ด๋ฒคํธ๋ ๋ธ๋ผ์ฐ์ ์ ๊ณ ์ ์ด๋ฒคํธ (nativeEvent
) ์๋ ๋ค๋ฆ
๋๋ค.
๋ชจ๋ ํฉ์ฑ ์ด๋ฒคํธ๋ BaseSyntheticEvent
๋ฅผ ์์๋ฐ์ผ๋ฉฐ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด์์ต๋๋ค.
interface BaseSyntheticEvent<E = object, C = any, T = any> {
nativeEvent: E;
currentTarget: C;
target: T;
bubbles: boolean;
cancelable: boolean;
defaultPrevented: boolean;
eventPhase: number;
isTrusted: boolean;
preventDefault(): void;
isDefaultPrevented(): boolean;
stopPropagation(): void;
isPropagationStopped(): boolean;
persist(): void;
timeStamp: number;
type: string;
}
React
์ ์ด๋ฒคํธ๋ค์ ํฌ๋ก์ค ๋ธ๋ผ์ฐ์ง์ ์ํด ๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ์์๋ ๊ฐ์ ์์ฑ์ ๊ฐ์ง๋๋ก ํ์คํํฉ๋๋ค.
React
์์ ์ง์ํ๋ ์ด๋ฒคํธ๋ค์ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด์์ผ๋ฉฐ ๋ชจ๋ EventHandler
ํ์
์ผ๋ก๋ถํฐ ์์ฑ๋ฉ๋๋ค.
type ReactEventHandler<T = Element> = EventHandler<SyntheticEvent<T>>;
type ClipboardEventHandler<T = Element> = EventHandler<ClipboardEvent<T>>;
type CompositionEventHandler<T = Element> = EventHandler<CompositionEvent<T>>;
type DragEventHandler<T = Element> = EventHandler<DragEvent<T>>;
type FocusEventHandler<T = Element> = EventHandler<FocusEvent<T>>;
type FormEventHandler<T = Element> = EventHandler<FormEvent<T>>;
type ChangeEventHandler<T = Element> = EventHandler<ChangeEvent<T>>;
type KeyboardEventHandler<T = Element> = EventHandler<KeyboardEvent<T>>;
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;
// ...
๊ทธ๋ฆฌ๊ณ EventHandler
๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด์์ต๋๋ค.
type EventHandler<E extends SyntheticEvent<any>> = { bivarianceHack(event: E): void }["bivarianceHack"];
์ฌ๊ธฐ์ SyntheticEvent
๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋์ด์์ต๋๋ค.
interface SyntheticEvent<T = Element, E = Event> extends BaseSyntheticEvent<E, EventTarget & T, EventTarget> {}
์ด๋ฒคํธ ํ์ดํ์ ์ด๋ป๊ฒ ํ๋๊ฒ ์ข์๊น?
React
๋ ์ค์ DOM API ๋ค์ ๋ํ ๋ํ ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ๊ณ ์๊ธฐ ๋๋ฌธ์ DOM ์ด๋ฒคํธ ํ์
์ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋์ ๋ชจ๋ ์ด๋ฒคํธ๋ค์ด SyntheticEvent
๋ฅผ ์์๋ฐ๊ณ ์๊ธฐ ๋๋ฌธ์
๋ค์๊ณผ ๊ฐ์ด name
์ด๋ value
๋ฑ์ ์์ฑ์ ์ ๊ทผํ ๋ ํ์
๋จ์ธ์ด ํ์ํฉ๋๋ค.
export default function ButtonExample() {
const handleClick = (e: SyntheticEvent) => {
const target = e.target as HTMLButtonElement // โ
type assertion
console.log(target.value)
}
return <button onClick={handleClick}>๋ฒํผ</button>
}
์ด๋ SyntheticEvent
์ target
์ด EventTarget
ํ์
์ด๋ฉฐ EventTarget
์
๋ค์๊ณผ ๊ฐ์ด name
์ด๋ value
์ ๊ฐ์ ํน์ ์๋ฆฌ๋จผํธ์์ ์ ๊ณตํ๋ ์ธํฐํ์ด์ค๊ฐ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ด๋ event.target
์ด ํญ์ DOM Element
์์ ๋ณด์ฅํ ์ ์๊ธฐ ๋๋ฌธ์, ์ด๋ ๊ฒ ๊ตฌ์กฐํ ๋์ด์๋ ๊ฒ์
๋๋ค.
interface EventTarget {
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
dispatchEvent(evt: Event): boolean;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}
๊ฐ์ธ ์ทจํฅ ์ฐจ์ด์ด์ง๋ง React
์์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๋ํ ํ์
๋ ์ ๊ณตํ๊ณ ์์ผ๋
๋ค์๊ณผ ๊ฐ์ด ํ์ ์ ์ ์ํ ์๋ ์์ ๊ฒ์ ๋๋ค.
const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => { /** */ }
๐ React ๋ด๋ถ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์คํ
React 17
๋ถํฐ ๋ชจ๋ ์ด๋ฒคํธ๋ document
๊ฐ ์๋ root DOM container
์ ํ ๋น๋ฉ๋๋ค.
์ฌ๊ธฐ์ root DOM
์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ํธ๋ฆฌ๊ฐ ๋ง์ดํธ๋๋ ์ง์ ์
๋๋ค.
๋๋ฌธ์ ์๋ฆฌ๋จผํธ์ ํ ๋นํ ๋ชจ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ root DOM Container
์ ํ ๋น๋ฉ๋๋ค.
const rootNode = document.getElementById('root');
ReactDOM.render(<App />, rootNode);
๐ก ์ ์ด๋ฌํ ์ ํ์ ํ ๊ฒ์ผ๊น?
๋ฆฌ์กํธ๋ ๋ค์๊ณผ ๊ฐ์ด ํน์ DOM ์์์ ์ด๋ฒคํธ๋ฅผ ํ ๋นํ๋๋ผ๋ ์ฑ๋ฅ์์ ์ด์ ๋ก
ํด๋น ๋ ธ๋์ ์ง์ ์ด๋ฒคํธ๋ฅผ ํ ๋นํ์ง๋ ์์ต๋๋ค.
customButton.addEventListener('click', handleClick)
React 17
์ด์ ์๋ document
๋
ธ๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ๋ event delegation
ํจํด์ ํ์ฉํ๊ณ ์์๋๋ฐ,
์ด๋ฌํ ๊ตฌ์กฐ๋ก ์ธํด ๋ช๊ฐ์ง ๊ฒฝ์ฐ์ ๋ํด์ ์ด์๊ฐ ๋ฐ์ํ์ต๋๋ค.
๐ค ๋ฌธ์ ์ํฉ
๋ง์ฝ ํ๋์ ์ฑ์์ ์๋ก ๋ค๋ฅธ ๋ฆฌ์กํธ ์ฑ ์ธ์คํด์ค๋ฅผ ์ค์ฒฉํ์ฌ ์ฌ์ฉํ ๋,
stopPropagation
์ ๋์์ด ์ฌ๋ฐ๋ฅด์ง ์์ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค๊ณ ํฉ๋๋ค.
์ด๋ฅผ ์ํด ํ ๊ฐ๋ฐ์๋ ๊ฐ๊ฐ์ ๋ฆฌ์กํธ ์ฑ์ iframe
์ผ๋ก ๊ฐ์ธ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋๋ฐ,
์ด๋ ๊ฒฐ๊ตญ iframe
์ด ์์ฑ ๋ฐ ํ๊ดด๋๋ ๊ณผ์ ์์ ๋ง์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ํด ์ฑ๋ฅ์ ์ข์ง์์ ์ํฅ์ ๋ฏธ์ณค๋ค๊ณ ํฉ๋๋ค.
https://github.com/facebook/react/pull/8117
https://github.com/facebook/react/issues/7128
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด react
์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ค์ document
๊ฐ ์๋
ํด๋น ๋ฆฌ์กํธ ํธ๋ฆฌ์ ๋ฃจํธ๋
ธ๋์ ํ ๋นํ๋๋ก React v17
๋ถํฐ ์์ ๋์๊ณ ,
์ด๋ฅผ ํตํด non-react(ex. jQuery) + react ๋ฅผ ํจ๊ป ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฆฌ์กํธ๊ฐ ์๋๋๋ก ๋์ํจ์ ๋ณด์ฅํ ์ ์๊ฒ ๋์์ต๋๋ค.
๐ Synthetic Event ์ Native Event ์ ๋งคํ ๊ณผ์
๋ฆฌ์กํธ๊ฐ ์ฒ๋ฆฌํ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ Synthetic Event
๋ฅผ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์
๊ฒฐ๊ตญ์๋ ์ด๋ฅผ Native Event
์ ๋งคํํ๋ ๊ณผ์ ์ด ํ์ํ ๊ฒ์
๋๋ค.
์ง๊ธ๋ถํฐ๋ ์ค์ react
์์ค ์ฝ๋๋ฅผ ๋ณด๋ฉด์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์ด๋ฒคํธ ํธ๋ค๋ง ๊ณผ์ ์ด ๋ถ์ฐฉ๋๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
โ ๏ธ ์ดํด๋๋ฅผ ๋์ด๊ธฐ ์ํด ํ๋ฆํ์ ์ ๋ถํ์ํ ์ฝ๋๋ ๊ฐ์ํํ์์ต๋๋ค.
์ฐ์ ๋ฆฌ์กํธ ๋ฃจํธ ๋ ธ๋๋ถํฐ ์ดํด๋ด ์๋ค.
React v18
๋ถํฐ๋ react-dom
ํจํค์ง์ createRoot
๋ผ๋ ํจ์๋ฅผ ํตํด ๋ฆฌ์กํธ ๋ฃจํธ๋
ธ๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด createRoot
ํจ์๋ ๋ด๋ถ์ ReactDomRoot.js
์ createRoot
ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
// @package/react-dom/src/client/ReactDom.js
import { createRoot as createRootImpl } from './ReactDOMRoot'
function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// ์ด๊ธฐ ์กฐ๊ฑด ์ฒดํฌํ๊ณ ...
return createRootImpl(container, options);
}
// @package/react-dom/ReactDomRoot.js
export function createRoot(
container: Element | Document | DocumentFragment,
options?: CreateRootOptions,
): RootType {
// ์ต์
๊ฐ ์ค์ , ์ด๊ธฐ ์กฐ๊ฑด ์ฒดํฌ ๋ฑ๋ฑ ...
const root = createContainer(
// ...
);
markContainerAsRoot(root.current, container);
if (enableFloat) {
Dispatcher.current = ReactDOMClientDispatcher;
}
const rootContainerElement: Document | Element | DocumentFragment =
container.nodeType === COMMENT_NODE
? (container.parentNode: any)
: container;
listenToAllSupportedEvents(rootContainerElement); // โ
return new ReactDOMRoot(root);
}
์ฌ๊ธฐ์ ์ค์ํ ๋ถ๋ถ์ ReactDOMRoot
๊ฐ ๋ฐํ๋๊ธฐ ์ด์ ์
listenToAllSupportedEvents
๋ก Native Event
๋ฅผ ๋ฃจํธ ๋
ธ๋์ ์ฐ๊ฒฐํ๋ ์์
์ ์ํํ๋ ๊ฒ์
๋๋ค.
์ด์ listenToAllSupportedEvents
๊ฐ ์ด๋ป๊ฒ ๊ตฌํ๋์ด์๋์ง ์ดํด๋ณผ๊ฒ์.
// @package/react-dom-bindings/src/events/DOMPluginEventSystem
export function listenToAllSupportedEvents(rootContainerElement: EventTarget) {
if (!(rootContainerElement: any)[listeningMarker]) {
(rootContainerElement: any)[listeningMarker] = true;
allNativeEvents.forEach(domEventName => {
if (domEventName !== 'selectionchange') {
if (!nonDelegatedEvents.has(domEventName)) {
listenToNativeEvent(domEventName, false, rootContainerElement);
}
listenToNativeEvent(domEventName, true, rootContainerElement);
}
});
// ...
}
}
์ฌ๊ธฐ์๋ ๋ชจ๋ Native Event
๋ค์ ๋ํด์ rootContainerElement
์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ถ์ฐฉํ๋ ๊ณผ์ ์ ์ํํฉ๋๋ค.
์ฌ๊ธฐ์ nonDelegatedEvents
๋ DOM ์์ ์ผ๊ด๋๊ฒ ๋ฒ๋ธ๋ง๋์ง ์๋ ์ด๋ฒคํธ๋ค์ด๊ธฐ ๋๋ฌธ์
ํด๋น root container ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋ถ์ฐฉํ์ง ์๊ณ ์ค์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ target element ์ ๋ถ์ฐฉํฉ๋๋ค.
๋ฐ๋ผ์ ์๋ฐ ์ด๋ฒคํธ๋ค์ capture phase
์ ํด๋นํ๋ ๋ฆฌ์ค๋๋ง,
๊ทธ ๋ฐ๋์ ๊ฒฝ์ฐ์๋ bubble phase
+ capture phase
์ ๋ฆฌ์ค๋๋ฅผ ๋ถ์ฐฉํฉ๋๋ค.
๐ Synthetic Event ํธ๋ค๋ง ๊ณผ์
synthetic event
์ native event
๊ฐ์ ๋งคํ ๊ณผ์ ์ ๋ํด์ ์ดํด๋ณด์์ผ๋,
์ด์ ์ค์ ๋ก ๋ธ๋ผ์ฐ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ react
๋ด๋ถ์์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ์ ์์๋ณด๊ฒ ์ต๋๋ค.
์์ native event
๋ฅผ synthetic event
์ ๋งคํํ ๋ listenToNativeEvent
ํจ์๋ฅผ ์ด์ฉํ๋๋ฐ,
์ฌ๊ธฐ์์ ๋ด๋ถ์ ์ผ๋ก addTrappedEventListener
ํจ์๋ฅผ ํธ์ถํฉ๋๋ค.
// @package/react-dom-bindings/src/events/DOMPluginEventSystem.js
function addTrappedEventListener(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
isCapturePhaseListener: boolean,
isDeferredListenerForLegacyFBSupport?: boolean,
) {
let listener = createEventListenerWrapperWithPriority(
targetContainer,
domEventName,
eventSystemFlags,
);
// ...
if (isCapturePhaseListener) {
if (isPassiveListener !== undefined) {
unsubscribeListener = addEventCaptureListenerWithPassiveFlag(/** */);
} else {
unsubscribeListener = addEventCaptureListener(/** */);
}
} else {
if (isPassiveListener !== undefined) {
unsubscribeListener = addEventBubbleListenerWithPassiveFlag(/** */);
} else {
unsubscribeListener = addEventBubbleListener(/** */);
}
}
}
์ฌ๊ธฐ์ createEventListenerWrapperWithPriority
๋ฅผ ํตํด listener
๋ฅผ ์์ฑํ์ฌ ํ ๋นํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
// @package/react-dom-bindings/src/events/ReactDOMEventListener.js
export function createEventListenerWrapperWithPriority(
targetContainer: EventTarget,
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
): Function {
const eventPriority = getEventPriority(domEventName);
let listenerWrapper;
switch (eventPriority) {
case DiscreteEventPriority:
listenerWrapper = dispatchDiscreteEvent;
break;
case ContinuousEventPriority:
listenerWrapper = dispatchContinuousEvent;
break;
case DefaultEventPriority:
default:
listenerWrapper = dispatchEvent;
break;
}
return listenerWrapper.bind(
null,
domEventName,
eventSystemFlags,
targetContainer,
);
}
์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด ์ด๋ฒคํธ์ ์ฐ์ ์์์ ๋ฐ๋ผ listenerWrapper
์ ์๋ง์ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํฉ๋๋ค.
์ฌ๊ธฐ์ ํ ๋น๋๋ ํธ๋ค๋ฌ๋ ๋ชจ๋ ์ต์ข
์ ์ผ๋ก dispatchEvent
๋ฅผ ํธ์ถํ๋ฉฐ
์ต์ข
์ ์ผ๋ก dispatchEventsForPlugins
๋ฅผ ํธ์ถํฉ๋๋ค.
// @package/react-dom-bindings/src/events/DOMPluginEventSystem.js
function dispatchEventsForPlugins(
domEventName: DOMEventName,
eventSystemFlags: EventSystemFlags,
nativeEvent: AnyNativeEvent,
targetInst: null | Fiber,
targetContainer: EventTarget,
): void {
const nativeEventTarget = getEventTarget(nativeEvent);
const dispatchQueue: DispatchQueue = [];
extractEvents(
dispatchQueue,
domEventName,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
targetContainer,
);
processDispatchQueue(dispatchQueue, eventSystemFlags);
}
getEventTarget
์์ native browser
์์ ์ผ๊ด๋ ๋ฐฉ์์ผ๋ก ์ด๋ฒคํธ ๋์ ์์๋ฅผ ์ถ์ถํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋น์ด์๋ dispatchQueue
๋ฅผ ์์ฑํ ๋ค, ์ด๋ฅผ ์ด์ฉํด SimpleEventPlugin ์ extractEvents
๋ฅผ ํธ์ถํฉ๋๋ค.
// @package/react-dom-bindings/src/events/plugins/SimpleEventPlugin.js
function extractEvents(
dispatchQueue: DispatchQueue,
domEventName: DOMEventName,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
targetContainer: EventTarget,
): void {
const reactName = topLevelEventsToReactNames.get(domEventName);
if (reactName === undefined) {
return;
}
let SyntheticEventCtor = SyntheticEvent;
let reactEventType: string = domEventName;
switch (domEventName) {
case 'keypress':
if (getEventCharCode(((nativeEvent: any): KeyboardEvent)) === 0) {
return;
}
case 'keydown':
case 'keyup':
SyntheticEventCtor = SyntheticKeyboardEvent;
break;
case 'focusin':
reactEventType = 'focus';
SyntheticEventCtor = SyntheticFocusEvent;
break;
case 'focusout':
reactEventType = 'blur';
SyntheticEventCtor = SyntheticFocusEvent;
break;
// ...
default:
break;
}
// ...
const listeners = accumulateSinglePhaseListeners(/** */);
if (listeners.length > 0) {
const event: ReactSyntheticEvent = new SyntheticEventCtor(/** */);
dispatchQueue.push({event, listeners});
}
}
extractEvents
์์๋ native event
์ ํด๋นํ๋ synthetic event
๋ฅผ ์์ฑํ์ฌ
์ด๋ฒคํธ ๋ฆฌ์ค๋์ ํจ๊ป ํ์ ํ ๋นํฉ๋๋ค.
์ด๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ accumulateSinglePhaseListeners
์์ ์ฐพ์ต๋๋ค.
ํด๋น ํจ์์์๋ ์ด๋ฒคํธ ํ์ผ์ ํธ์คํธ ์ปดํฌ๋ํธ ๋ฐ ์ค์ฝํ์ ํด๋นํ๋ ๋ชจ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ฐฐ์ด์ ๋ง๋ค์ด์ ๋ฐํํด์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ค์ dispatchEventsForPlugins
๋ก ๋์๊ฐ,
processDispatchQueue
์์ ํ์ ํ ๋น๋ ํธ๋ค๋ฌ๋ค์ ์์์ ๋ง๊ฒ ํธ์ถํด์ค๋๋ค.
๐ Event Pooling (โ ๏ธ removed in React v17)
React 17
์ด์ ์๋ SyntheticEvent
์ฌ์ฉ์ ๋ฐ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ๋ฅผ ์ํด
Event Pooling
์ด๋ผ๋ ์์คํ
์ ์ฌ์ฉํ์ต๋๋ค.
ํ์ง๋ง ์ด๋ ๋์ ๋๋ ์ฑ๋ฅ ํฅ์์ ๊ฐ์ ธ์ค์ง ๋ชปํ๊ณ , ์ฌ๋ฌ ๋ฆฌ์กํธ ๊ฐ๋ฐ์๋ค์๊ฒ ํผ๋์ ์ฃผ๋ ๊ฒฝ์ฐ๊ฐ ์๊ฒผ์ต๋๋ค.
์ด๋ ๋ฆฌ์กํธ๊ฐ ์๋ก ๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ์ด๋ฒคํธ์ ๋ํด์ ์ด๋ฒคํธ ๊ฐ์ฒด
๋ฅผ ์ฌํ์ฉํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ ๊ฒ์
๋๋ค.
๋ฆฌ์กํธ๋ ์ค์ native ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ํ๋ synthetic event ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์
์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค ์ธ์คํด์ค๋ฅผ ์์ฑํด์ผํ๊ณ , ํ์ ์์ด์ง ๊ฒฝ์ฐ์๋ GC ์ ์ํด ์ญ์ ๊ฐ ํ์ํด์ง๋๋ค.
์ด ๊ฒฝ์ฐ ์ด๋ฒคํธ๊ฐ ์์์ด ๋ง์์ง๋ค๋ฉด ๋ฉ๋ชจ๋ฆฌ์ ๋ถ๋ด์ ์ค ์ ์์ผ๋ฉฐ,
GC ๋ํ ๋ง์ ์ํ์ด ํ์ํด์ ธ CPU ์๊ฐ์ ๋ง์ด ์๋ชจํ๊ธฐ ๋๋ฌธ์
์ด๋ฒคํธ ์ธ์คํด์ค๋ฅผ ์ฌํ์ฉํ๋ synthetic instance pool
์ ์ ์งํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ํ์ต๋๋ค.
์ด๋ฌํ ๋ฉ์ปค๋์ฆ์ ํตํด GC ์ ์คํ ํ์๋ฅผ ์ต์ํํ๊ณ
๋์ฑ์ด ๊ฐ๊ฐ์ ์ด๋ฒคํธ๋ง๋ค ์ธ์คํด์ค๋ฅผ ์์ฑํ์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์์ ์ด์ ์ ๊ฐ์ ธ์ฌ ์ ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ์ต๋๋ค.
ํ์ง๋ง ์ด๋ฌํ ์ค๊ณ๋ ๋น๋๊ธฐ ์ฝ๋ฐฑ
์ ๋ค๋ฃจ๋ ๊ฒฝ์ฐ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๋ค์ ์ฝ๋๋ React v16
์์ ์ด๋ค ๋ฌธ์ ์ ์ ๊ฐ์ง๊ณ ์์๊น์?
export default function Input() {
const [data, setData] = useState('')
const handleChange = (e) => {
this.setData(() => ({
name: event.target.value
}));
}
}
๋ฌธ์ ๋ setState
์ updater
ํจ์๋ฅผ ๋๊ฒจ์ฃผ๊ณ , ์ฌ๊ธฐ์ event.target
์ ์ ๊ทผํ์ฌ ์๊ฒผ์ต๋๋ค.
์ด๋ ๋ฆฌ์กํธ๊ฐ ํน์ ์ด๋ฒคํธ์ ํธ๋ค๋ฌ๋ฅผ ์คํ์ํจ ๋ค ๋ค์ pool ์ ๋ฃ๊ธฐ ์ํด
event ์ ๋ชจ๋ ์์ฑ๋ค์ null ๋ก ์ด๊ธฐํ ์ํค๊ธฐ ๋๋ฌธ์ ์ฐธ์กฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ ๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด event.persist ๋ฅผ ํตํด ์ด๋ฒคํธ ์ฐธ์กฐ๋ฅผ ์ ์งํ๋ค๊ณ ์๋ ค์ฃผ๊ฑฐ๋
value ๋ฅผ ๋ฏธ๋ฆฌ ์ฐธ์กฐํ๊ณ ์๋ ๋ณ์๋ฅผ ํ ๋นํ์ฌ ์ฐธ์กฐํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํฉ๋๋ค. (ํด๋ก์ )
// React v17 ์ด์ ์๋..
handleChange(event) {
event.persist(); // โ
persist() ์ฒ๋ฆฌ๋ฅผ ํ๊ฑฐ๋,
this.setData(() => ({ name: event.target.value }));
}
handleChange(event) {
const name = event.target.value // โ
ํน์ ํด๋ก์ ๋ฅผ ํ์ฉํ์ต๋๋ค.
this.setData(() => ({ name }));
}
๐ ์ฐธ๊ณ ์๋ฃ
์ด๋ฒคํธ ์ฒ๋ฆฌํ๊ธฐ - React
React v17.0 Release Candidate: No New Features - React Blog
How to perform event handling in React [Tutorial] | Packt Hub
Web: React์ Event ์์คํ ๋ด๋ถ ๊ตฌํ ์์ธํ ์์๋ณด๊ธฐ (React v18.2.0)
Why does the newer 'setState' method not recognize my event.target?
React Deep Dive- React Event System (1)
Web: React์ Event ์์คํ ๋ด๋ถ ๊ตฌํ ์์ธํ ์์๋ณด๊ธฐ (React v18.2.0)
'๐จโ๐ป web.dev > fe' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Next.js styled-component ์ค์ ํ๊ธฐ (SSR FOUC ์ด์) (0) | 2022.11.27 |
---|---|
AWS SDK ๋ก s3 ํ์ผ ์ฌ์ด์ฆ ์กฐํํ๊ธฐ (0) | 2022.11.26 |
File API ๋ฅผ ์ด์ฉํ Input ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ (2) | 2022.08.13 |
Next.js API Routes ์์๋ณด๊ธฐ (2) | 2022.07.26 |
React ์ปดํฌ๋ํธ(children) ํ์ดํํ๊ธฐ (0) | 2022.07.23 |
๐ฌ ๋๊ธ