https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
λͺ¨λ κ²μ κΈ°λ³Έμ μΌλ‘ μλμ μΌλ‘ μ€μ νκ³ νμν λλ§ νμ±ννλλ‘ μ ννλ κ²μ΄ μ’μ΅λλ€. μλ₯Ό λ€μ΄ ν μ€νΈ μ λ ₯ μ΄λ²€νΈλ₯Ό μμ ν μ μμ§λ§ νμ± λ¦¬μ€λκ° μλ κ²½μ°μλ preventDefault λλ μ μ΄λ λμλ§ μ¬μ©ν μ μμ΅λλ€.
λ§μ°¬κ°μ§λ‘ μ΄κ²μ React Nativeμ μ€λ λ© λͺ¨λΈκ³Ό ν΅ν©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ μ°λ¦¬κ° ν μ μλ ν κ°μ§λ ν€ μ λ ₯ μ²λ¦¬μ κ°μ νμ± λ¦¬μ€λκ° μμ λ UI μ€λ λλ₯Ό λκΈ°μ μΌλ‘ μ°¨λ¨νλ κ²μ λλ€.
cc @vjeux @ide
μ΄κ²μ Chrome 51μ μλ₯νμ΅λλ€. Reactμμ μ΄λ₯Ό μ§μνκΈ° μν μ λ°μ΄νΈλ κ³νμ΄ μμ΅λκΉ? :μν
Reactκ° λ¬Έμμ μ΄λ²€νΈ 리μ€λκ° νλλ§ μκ³ λ€λ₯Έ μ¬λμκ² μμνλ κ²½μ° μ΄κ²μ΄ μ΄λ»κ² κ°λ₯ν©λκΉ?
@sebmarkbage
μλ μ΄λ²€νΈ λ¬Έμ μ νμ¬ μνλ 무μμ λκΉ?
μλ μ΄λ²€νΈ νΈλ€λ¬λ‘ λ±λ‘λ κ²½μ° μ΅μ νλ μ μλ ν μ΄λ²€νΈ μ²λ¦¬μ λν΄ ν¬λ‘¬μμ κ²½κ³ λ₯Ό λ°μμ΅λλ€. λ°λΌμ Reactμμ μ΄κ²μ κ°λ κ²μ κΉλν κ²μ λλ€!
λν μΌκ°μ Firefoxμ μ΄λ―Έ λμ°©ν once
μ κ°μ μμμ μ΅μ
μ μ²λ¦¬νκ³ μΆμ μλ μμ΅λλ€: https://twitter.com/mozhacks/status/758763803991474176. μ 체 λͺ©λ‘: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
FWIW, Facebookμ μ¬μ΄λλ° λλ μ±ν μ°½μ΄ μ€ν¬λ‘€λ λ μΈλΆ μ€ν¬λ‘€μ μ°¨λ¨νκΈ° μν΄ νμ± ν μ΄λ²€νΈλ₯Ό μμ ν©λλ€. UI μμ΄λ UIλ₯Ό ꡬνν μ μμ΅λλ€. μ°λ¦¬λ μ¬μ ν μ΄κ²μ μ΅μ μΌλ‘ μ§μνκΈ°λ₯Ό μνμ§λ§ λ¬Έμ 곡κ°μ μ¬μ ν ββλΆμμ νλ―λ‘ μλ μ΄λ²€νΈ 리μ€λλ₯Ό ν¬ν¨νμ§ μλ μ΄ λ¬Έμ μ λν λμ μ루μ μ΄ λ°μ ν μ μμ΅λλ€. κ·Έλμ μ¬μ ν νλ°ν λμμΈ κ³΅κ°μ λλ€.
λ νμ± μ²μ·¨μλ₯Ό μ μ§νκ³ μ§μ μλ μ²μ·¨μλ₯Ό μΆκ°νλ κ²μ΄ μ€μν©λλ€.
λ°μ€ν¬ν± μμ© νλ‘κ·Έλ¨μμλ μ°¨μ΄κ° λνλμ§ μμ§λ§ λͺ¨λ°μΌ μμ© νλ‘κ·Έλ¨μμλ μλ μ€ν¬λ‘€ μμ κΈ°κ° μλλ₯Ό ν¬κ² ν₯μμν΅λλ€.
μμ μ μ:
<SomeElement
onScroll={this.onScrollThatCallsPreventDefault}
onScrollPassive={this.onScrollThatJustListens}
...this.props
/>
@romulof μ, μΊ‘μ² λ¨κ³μμλ μ΄λ²€νΈλ₯Ό λ±λ‘νλ λ°©λ²μ λλ€.
<SomeElement
onClick={this.onClick}
onClickCapture={this.onClickCapture}
onScrollPassive={this.onScrollPassive}
/>
κ·Έλμ μ΄κ²μ΄ ν¨μλΈ μ΄λ²€νΈλ μ§μνλ μ μ ν APIκ° λ κ²μ΄λΌκ³ μκ°ν©λλ€.
μ°Έκ³ μ¬ν: κΉλ€λ‘μ΄ μ§λ¬Έμ - μΊ‘μ² λ¨κ³μμ μλ μ΄λ²€νΈλ₯Ό μ΄λ»κ² λ±λ‘ν©λκΉ? μκ·Ήμ μΈ μ¬κ±΄μ νΉμ±μ λΆκ°λ₯ν μΌμ΄λΌκ³ μκ°ν©λλ€. event.preventDefault()
λ₯Ό νΈμΆνλ κ²μ‘°μ°¨ νμ©λμ§ μκΈ° λλ¬Έμ μ΄κ²μ μλ§λ λ¬Έμ κ° λμ§ μμ κ²μ
λλ€.
@radubrehar , onScrollCapturePassive
λ λν μΌμ΄μ€μ μ 체 μ±κ²½μ²λΌ 보μ
λλ€.
:) μΊ‘μ² λ¨κ³μ μλ μ΄λ²€νΈκ° μκΈ° λλ¬Έμ κ·Έλ μ§ μμ΅λλ€.
λ¬Όλ‘ μλ―Έκ° μμ§λ§, λλ κ·Έκ²μ μμ‘΄νμ§ μμ κ²μ
λλ€. once
μ κ°μ λ€λ₯Έ μ νμ μ΄λ²€νΈ λ°μΈλ©λ μμ΅λλ€.
λ λ€λ₯Έ μ μ:
<SomeElement
onScroll={this.onScrollThatCallsPreventDefault}
/>
<SomePassiveElement
onScroll={{
passive: true,
capture: true,
handler: this.onScrollThatJustListens,
}}
/>
μ΄λ° μμΌλ‘ Reactλ μ΄λ²€νΈ νΈλ€λ¬κ° ν¨μμΈμ§(μΌλ° λ°μΈλ©) λ°μΈλ© μ΅μ κ³Ό νΈλ€λ¬ ν¨μλ₯Ό ν¬ν¨νλ κ°μ²΄μΈμ§ κ°μ§ν΄μΌ ν©λλ€.
νμν μ μλ λ€λ₯Έ μ΅μ
μ΄ μκΈ° λλ¬Έμ μ΅μ
μ΄ μλ κ°μ²΄ μ κ·Ό λ°©μμ΄ onFooPassive
λ³΄λ€ λ ν©λ¦¬μ μ΄λΌκ³ μκ°ν©λλ€. μ΄λ²€νΈκ° κΈ°λ³Έμ μΌλ‘ μλμ μ΄μ΄μΌ νλ€λ @sebmarkbage μ μ μκ³Ό κ²°ν©νλ©΄
μκ°λλ λ λ€λ₯Έ μ κ·Ό λ°©μμ μ΄λ²€νΈ νΈλ€λ¬μ μμ±μ μΆκ°νμ¬ ν¨μλΈ λͺ¨λμμ μ΅νΈμμ(λλ λ€λ₯Έ μ΅μ μ ν κΈ)ν μ μλλ‘ νλ κ²μ λλ€. μ΄ κ°μ:
class Foo extends React.Component {
constructor() {
this.handleScroll = this.handleScroll.bind(this);
this.handleScroll.passive = false;
}
handleScroll() {
...
}
render() {
return <div onScroll={this.handleScroll} />;
}
}
μ΄λ‘ μ μΌλ‘ μ΄κ²μ λ°μ½λ μ΄ν°κ° μ°©λ₯νλ©΄ κ½€ μ μλν©λλ€.
μ΄μ λν΄ μ‘°κΈ λ μκ°ν΄λ³΄λ©΄ κ°λ³ μ΅μ 보λ€λ μ΄λ²€νΈ μ΅μ μμ±μ ν¨μμ μΆκ°νλ κ²μ΄ λ λμ κ² κ°λ€. κ·Έλ¬λ©΄ Reactλ μ μ¬μ μΌλ‘ λ§μ μμ± λμ μ νλμ μμ±λ§ κ±±μ νλ©΄ λ©λλ€. λ°λΌμ μμ μλ₯Ό μ‘°μ νλ €λ©΄ λ€μμ μννμμμ€.
class Foo extends React.Component {
constructor() {
this.handleScroll = this.handleScroll.bind(this);
this.handleScroll.options = { passive: false };
}
handleScroll() {
...
}
render() {
return <div onScroll={this.handleScroll} />;
}
}
λ λ€λ₯Έ μκ°μ μ΄λ¬ν μ΅μ μ΄ JSXλ₯Ό ν΅ν΄ μ λ¬λλλ‘ νμ©νλ λ°©μμΌλ‘ JSX ꡬ문 μ μκ²ΌμκΉ νλ κ²μ λλ€. λ€μμ λ΄κ° λ§μ΄ μκ°νμ§ μμ μμμ μμ λλ€.
return <div onScroll={this.handleScroll, { passive: false }} />;
λλ λν μ΄λ²€νΈκ° κΈ°λ³Έμ μΌλ‘ μλμ μ΄μ΄μΌ νλμ§ μ¬λΆμ λν΄ μκ°νκ³ μμΌλ©°, λλ μ½κ° κ²½κ³νκ³ μμ΅λλ€. ννΈμΌλ‘ μ΄κ²μ μ€ν¬λ‘€ νΈλ€λ¬μ κ°μ μ΄λ²€νΈμ λΆλͺ ν μ’μ κ²μ΄μ§λ§ λ§μ ν΄λ¦ νΈλ€λ¬μ λ무 λ§μ λκΈ°λ₯μ μκΈ°μΉ μμ λμμ μΌμΌν¬κΉ κ±±μ λ©λλ€. μΌλΆ μ΄λ²€νΈλ κΈ°λ³Έμ μΌλ‘ μλμ μ΄κ³ λ€λ₯Έ μ΄λ²€νΈλ κ·Έλ μ§ μλλ‘ λ§λ€ μ μμ΅λλ€.
μ΄ λ°©λ²μ JSX ꡬ문μ μμ νμ§ μκ³ μ΄μ μ μ μν κ²κ³Ό λ§€μ° μ μ¬ν©λλ€.
return <div onScroll={{ handler: this.handleScroll, passive: true }} />;
κ·Έλ¦¬κ³ λ¬Έμνλ κ°λ¨ν κ²μ λλ€:
div.propTypes = {
...
onScroll: React.PropTypes.oneOf([
React.PropTypes.func,
React.PropTypes.shape({
handler: React.PropTypes.func.isRequired,
capture: React.PropTypes.bool,
passive: React.PropTypes.bool,
once: React.PropTypes.bool,
}),
};
λ°μ μ΄λ²€νΈλ κΈ°λ³Έμ μΌλ‘ μλμ μ
λκΉ? μ μ΄λ ν°μΉ μ΄λ²€νΈμμλ κ·Έλ° κ² κ°μ΅λλ€. λ°λλΌ λ¬Έμ μμ€ μ΄λ²€νΈ 리μ€λλ‘ λμκ°μ§ μλ ν preventDefault
ν μ μμ΅λλ€.
@joshjg React νΈλ€λ¬λ λ€μ΄ν°λΈ μ΄λ²€νΈμ λΉμ·νμ§λ§ λ€λ₯Έ "ν©μ± μ΄λ²€νΈ"λ₯Ό μ λ¬
λλ ꡬν μΈλΆ μ¬νμ μ΅μνμ§ μμ§λ§ λ°©μ§νλ €λ νΈλ€λ¬κ° React μ΄λ²€νΈ νΈλ€λ¬μΈ ν μ΅μν preventDefault
μλνλ€λ κ²μ μκ³ μμ΅λλ€. μ΄μ¨λ μ κ²½νμ΄μμ΅λλ€.
stopPropagation
νλ©΄ μ΄μ΄ μ’μ§ μμ κ°λ₯μ±μ΄ λ λμ΅λλ€(μ: Reactμ κ²°ν©ν μ μλ document
ν΄λ¦ 리μ€λκ° μκ³ λ΄λΆλ₯Ό ν΄λ¦ν λ λ²λΈλ§μ λ°©μ§νκ³ μΆμ΅λλ€. νΉμ μμ). μ΄ κ²½μ° λ€μμ μ¬μ©ν μ μμ΅λλ€.
function stopPropagation (e) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
}
[[MDN](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation)]
μ΄κ²μ μ£Όμ μ£Όμ μμ μ½κ° λ²μ΄λ¬μ§λ§ 짧μ λλ΅μ Reactκ° μλ μ΄λ²€νΈλ₯Ό μ¬μ©νμ§ μκ³ λλλ‘ μ΄μν μμλ‘ μ²λ¦¬λλ€λ κ²μ λλ€.
@joshjg @benwiley4000 @gaearon μ΅κ·Όμ ν¬λ‘¬ νμ λ¬Έμ μμ€ ν°μΉ μ΄λ²€νΈμ λν μ κ·Ό λ°©μμ λ³κ²½νμ¬ κΈ°λ³Έμ μΌλ‘ μλμΌλ‘ λ§λ€μμ΅λλ€. κ·Έλ¦¬κ³ Reactλ λ¬Έμ μμ€μμ μ΄λ²€νΈλ₯Ό 첨λΆνλ―λ‘ μ΄ μλ‘μ΄ λμμ μ»κ² λ©λλ€.
https://www.chromestatus.com/features/5093566007214080 μ°Έμ‘°
μ΄κ²μ κ°μ μ μΌλ‘ Reactκ° λμνλ λ°©μμ λ³κ²½νμ΅λλ€. Reactκ° μ΄λ²€νΈλ₯Ό 첨λΆν λ passive: false
λͺ
μμ μΌλ‘ μΈκΈνμ§ μλλ€κ³ κ°μ ν©λλ€. λ°λΌμ λμμ΄ λ³κ²½λ©λλ€.
λλ λ°©κΈ μ΄κ²μ μ³€λ€ - κ·Έλμ λΉμ μ addEventListenerλ₯Ό μ¬μ©νμ¬ μμΌλ‘ ν°μΉ μ΄λ²€νΈλ₯Ό λ±λ‘ν΄μΌ νλ€
μ°Έκ³ λ‘ Edge 16 μ μλ μ΄λ²€νΈ 리μ€λλ₯Ό μ 곡 ν©λλ€. μμ§ μ§μνμ§ μλ μ£Όμ λΈλΌμ°μ μ€
ν¬λ‘¬ μλ λ³ κΈ°λ³Έ κ°μ
μλ§ μ μ©λ©λλ€ touchstart
λ° touchmove
νμ§ wheel
. λ°λΌμ λͺ
μμ μΈ {passive: true}
κ° μλ wheel
μ΄λ²€νΈλ λ§μ°μ€ν λ° λ μκ°λ½ νΈλν¨λ μ€ν¬λ‘€μ λν΄ λκΈ° μ€ν¬λ‘€μ κ³μ κ°μ μ€νν©λλ€. (μ¬κΈ°μ λͺ κ°μ§ λ―Έλ¬ν λΆλΆμ λν΄ λΈλ‘κ·Έ κ²μλ¬Όμ μμ±νμ΅λλ€.)
λν μ°λ¦¬(Edge ν)λ λμΌν κ°μ
μ ꡬνν μλκ° μμΌλ―λ‘ μλ μ΄λ²€νΈ 리μ€λλ₯Ό μ 곡ν λ {passive: true}
λ₯Ό λͺ
μμ μΌλ‘ μ§μ νκΈ°λ₯Ό μν κ²μ
λλ€.
μ°Έκ³ λ‘, μ€ν¬λ‘€ divκ° μμ λ λͺ¨λ°μΌμμ λ³Έλ¬Έμ΄ μ€ν¬λ‘€λλ κ²μ λ°©μ§νκΈ° μν΄ passive: false κ²½λ‘λ‘ μ΄λνκΈ° μμνμ§λ§, μ€ν¬λ‘€μ μ°¨λ¨νκΈ° μν΄ preventDefault()λ₯Ό μ¬μ©νλ κ²μ μ½κ° 무κ²μ΅λλ€. divκ° μλμ§ μ¬λΆμ λ°λΌ νΈλ€λ¬λ₯Ό μΆκ° λ° μ κ±°νκ±°λ body.height = 100% μ κ·Ό λ°©μμΌλ‘ λ체ν μ μμ΅λλ€. body.height μμ μ μ½κ° ν΄νΉλ λλμ΄ λ€μ§λ§ μλ: falseκ° μ ν νμνμ§ μμ΅λλ€.
λ΄ μ¬μ© μ¬λ‘λ event.preventDefault()
λ©μλλ₯Ό μ¬μ©νμ¬ μ¬μ©μκ° λ΄λΆμμ μμλ₯Ό λ λ 컨ν
μ΄λ μ€ν¬λ‘€μ λ°©μ§νκ³ μΆμ΅λλ€.
μ΄λ₯Ό μν΄μλ μ΄λ²€νΈ 리μ€λλ₯Ό non-passive(passive: false)λ‘ λ±λ‘ν΄μΌ ν©λλ€.
λΈλΌμ°μ κ° μλμΌλ‘ μ νν¨μ λ°λΌ κΈ°λ³Έμ μΌλ‘ true, λλ λ°λλ‘ ν μ μκΈ°λ₯Ό μν©λλ€.
λΆννλ touch-action: none;
μ€νμΌμ μ¬μ©ν μ μμ΅λλ€. ν°μΉκ° μμλ νμ μ μ©λκ³ μκ³ μλ§λ μλ¬΄λ° ν¨κ³Όκ° μκΈ° λλ¬ΈμΌ κ²μ
λλ€.
κ·Έκ²μ μ λ§λ‘ 곧 λ¬Έμ κ° λ κ²μ λλ€. μ λ 2λ λμ ν΄κ²°μ± μ μ°Ύμ§ λͺ»νλ€λ μ¬μ€μ λλμ΅λλ€. κ·Έλ¦¬κ³ μλμΌλ‘ μ΄λ²€νΈ 리μ€λλ₯Ό μμ±νλ κ²μ Reactμμ μν° ν¨ν΄μ λλ€.
κ·Έλ¦¬κ³ κ·Έκ²μ΄ μ£Όμ λ³κ²½ μ¬νμ μμ±νλ κ²½μ°μλ λ§μ°¬κ°μ§μ λλ€. κ·Έλλ μ΄μΌκΈ°μ μΌλΆλ₯Ό λμΉ μ μμ΅λλ€.
https://github.com/facebook/react/issues/6436#issuecomment -254331351μμ @romulof κ° μ μν μλ‘μ΄ μ΄λ²€νΈ 리μ€λ μλͺ
μ΄
μ¬κΈ°μ μ€λͺ
λ λ¬Έμ λ₯Ό μμ νλ κ² μΈμλ once
μ κ°μ λ€λ₯Έ EventListenerOptionsλ₯Ό μ§μ ν μ μμ΅λλ€.
λ°©κΈμ΄ λ¬Έμ κ° λ°μνμ΅λλ€. μ¬μ©μκ° κ·Έλ¦΄ μ μλ μΊλ²μ€κ° μμ΅λλ€. Androidμμ κ·Έλ¦Όμ 그릴 λ νμΈνΈ μΉ μ νλ λμ 'μλ‘ κ³ μΉ¨'μ νλ κ²½μ°κ° μμ΅λλ€. μ΄κ²μ κ·Έκ²μ΄ νμ€ μΈκ³μ λ¬Έμ μμ 보μ¬μ€λλ€. μ§κΈμ onTouch{Start,Move,End}
μ μ¬μ©νμ§ μκ³ μλμΌλ‘ addEventListener
λ₯Ό ν΄κ²° λ°©λ²μΌλ‘ μ¬μ©νκ² μ΅λλ€.
@romulofκ° μ μν μ κ·Ό λ°©μμ λ§€μ° μ’μν©λλ€. μ루μ μλ μ£Όμ λ³κ²½ μ¬νμ΄ νμνμ§ μμ κ² κ°μ΅λλ€.
@bobvanderlinden touch-action: none;
μ μΊλ²μ€ μ€νμΌμ μΆκ°νλ©΄ ν¨κ³Όκ° μμ κ²μ
λλ€. ν΄λΉ μ¬μ© μ¬λ‘μμ μ λ§ λΉμ λ°ν©λλ€. λ€λ₯Έ κ°λ₯ν κ° λ λ§€μ° νΈλ¦¬ν μ μμ΅λλ€.
κ·Έλ¬λ @piotr-czκ° μ§μ νλ―μ΄ touch-action
λ μ΄ μλ μ΄λ²€νΈ λ¬Έμ λ₯Ό μ 체μ μΌλ‘ ν΄κ²°νμ§ λͺ»ν©λλ€. λν μμ μμλ₯Ό λλκ·Ένλ λμ 컨ν
μ΄λκ° μ€ν¬λ‘€λλ κ²μ λ°©μ§νλ λμΌν λ¬Έμ μ μ§λ©΄νκ³ μμ΅λλ€. λͺ¨λ ν΄κ²° λ°©λ²μ κ½€ ν΄ν€νκ³ κΈ°μ μ λΆμ±λ₯Ό μΆκ°ν©λλ€.
λΆννλ μλμ μ΄μ§ μμ μμ κΈ°κ° μμΌλ©΄ μ€μ λ‘ μ°κ²°λ μ¬μ©μ μμ μ²λ¦¬κΈ°κ° μλλΌλ μ¬κ°ν λ²λ²
κ±°λ¦Όμ΄ λ°μν μ μμ΅λλ€. Chromeμ verbose
λ‘κ·Έ μμ€μμ μ΄μ λν΄ μλ €μ€λλ€. [Violation] Handling of 'wheel' input event was delayed for 194 ms due to main thread being busy. Consider marking event handler as 'passive' to make the page more responsive.
(μ΄κ²μ https://github.com/facebook/react/blob/92b7b172cce9958b846844f0b46fd7bbd8c5140d/packages/μμ μΆκ°ν μ΅μμ μ²λ¦¬κΈ°μ
λλ€. react-dom/src/events/ReactDOMEventListener.js#L155)
@romulof @lencioni @radubrehar ν¨μλΈ νλκ·Έκ° μ€ν¬λ‘€ μ΄λ²€νΈ 리μ€λμμ μ¬μ©νκΈ° μν κ²μ΄ μλλΌλ μ¬μ€μ μκ³ κ³μλκΉ? λΈλΌμ°μ μ μ€ν¬λ‘€ μ±λ₯μ λ°©ν΄νμ§ μμΌλ €λ©΄ touchmove
λ±κ³Ό κ°μ μ΄λ²€νΈμ μ¬μ©ν΄μΌ ν©λλ€. λΉμ μ μλ λμκ² λ§€μ° νΌλ μ€λ½μ΅λλ€.
κΈ°λ³Έ μ€ν¬λ‘€ μ΄λ²€νΈμμλ μλ μ€μ μ΄ μ€μνμ§ μμ΅λλ€. μ·¨μν μ μμΌλ―λ‘ ν΄λΉ 리μ€λκ° νμ΄μ§ λ λλ§μ μ°¨λ¨ν μ μκΈ° λλ¬Έμ λλ€.
μΆκ° μ 보: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
μ, μ λ μ΄μ μ΄κ²μ μκ³ μμ΅λλ€. μ κ° μμ μ κΈμ μΌμ λ μλͺ» μκ³ μμμ΄μ.
μ½λ©νΈ. μ€λͺ
ν΄μ£Όμ
μ κ°μ¬ν©λλ€!
2017λ
12μ 6μΌ μμμΌ, μ€μ 8:25 Martin Hofmann [email protected]
μΌλ€:
@romulof https://github.com/romulof @lencioni
https://github.com/lencioni @radubrehar https://github.com/radubrehar
μλ νλκ·Έκ°
μ€ν¬λ‘€ μ΄λ²€νΈ 리μ€λ? touchmove λ±κ³Ό κ°μ μ΄λ²€νΈμ μ¬μ©ν΄μΌ ν©λλ€.
λΈλΌμ°μ μ μ€ν¬λ‘€ μ±λ₯μ λ°©ν΄νμ§ μμ΅λλ€. λΉμ μ μ
λμκ² λ§€μ° νΌλ μ€λ½μ΅λλ€.κΈ°λ³Έ μ€ν¬λ‘€ μ΄λ²€νΈμμλ μλ μ€μ μ΄ μ€μνμ§ μμ΅λλ€.
μ·¨μλμ΄ λ¦¬μ€λκ° νμ΄μ§ λ λλ§μ μ°¨λ¨ν μ μμ΅λλ€.μΆκ° μ 보:
https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.mdβ
λΉμ μ΄ μΈκΈλμκΈ° λλ¬Έμ μ΄κ²μ λ°λ κ²μ λλ€.μ΄ μ΄λ©μΌμ μ§μ λ΅μ₯νκ³ GitHubμμ νμΈνμΈμ.
https://github.com/facebook/react/issues/6436#issuecomment-349691618 ,
λλ μ€λ λ μμκ±°
https://github.com/notifications/unsubscribe-auth/AAL7zgbNCpHNui-TX7r2FYdhVxZfdBX8ks5s9r_4gaJpZM4ICWsW
.
@el-moalo-loco, μ±λ₯ ν₯μμ μν΄ μ€ν¬λ‘€ μ΄λ²€νΈμ μλ 리μ€λλ₯Ό μ¬μ©νλ λ°©λ²μ λν΄ Google κ°λ°μ μ¬μ΄νΈμμ μΌλΆ λ¬Έμλ₯Ό μ½μλ€κ³ νμ ν©λλ€. λ΄κ° μλͺ» μ½μκ±°λ κ·Έ κ³Όμ μμ 무μΈκ°κ° λ³κ²½λμμ κ²μ λλ€. μ΄μ¨λ λ§μ μ€λͺ κ°μ¬ν©λλ€!
@romulof @lencioni @el-moalo-loco https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners
μ€ν¬λ‘€ 리μ€λκ° νμνμ§ μλλΌλ ν 리μ€λλ μ¬μ ν μλμ μ΄μ΄μΌ ν©λλ€. κ·Έκ²μ΄ νΌλμ μμΈμ΄ λ μ μλ€κ³ μκ°ν©λλ€.
@sebmarkbage μ΄λ»κ² μκ°νμΈμ? μ΄ ν¨μλΈ μ΄λ²€νΈ 리μ€λ μ§μμ΄ μΈμ κ°λ Reactλ‘ λ€μ΄κ°κΉμ?
μλ νμΈμ,
λ€μκ³Ό κ°μ΄ componentDidMount
μ νμ± μ΄λ²€νΈ 리μ€λλ₯Ό μΆκ°ν΄μΌ νμ΅λλ€.
global.addEventListener("touchstart", this.touchStart(), { passive: false })
e.preventDefault()
λ₯Ό νΈμΆνμ¬ Chromeμ κΈ°λ³Έ μ€ν¬λ‘€μ μ€μ§νκ³ touchStart()
μ μμλ₯Ό μ΄λν μ μμ΅λλ€.
μ΄λ€ μμλ₯Ό μ΄λν΄μΌ νλμ§ μκΈ° μν΄ λ€μκ³Ό κ°μ΄ JSX μμμ onTouchStart
λ₯Ό μΆκ°ν΄μΌ νμ΅λλ€.
onTouchStart={this.touchStartSetElement(element)}
touchStartSetElement()
μμ touchStart()
μμ μ½μ μ μλ μν μμ± element
μ μ€μ νμ΅λλ€.
Reactκ° νμ± μ΄λ²€νΈ 리μ€λλ₯Ό μ§μνλ€λ©΄ μ΄κ²μ ν μ€λ‘ μμ½λ κ²μ λλ€.
κ°μ¬ ν΄μ,
ν립
μΆμ : μλ μ΄λ²€νΈ 리μ€λμμ e.preventDefault()
λ₯Ό νΈμΆνλ €κ³ νλ©΄ Chrome 56μμ λ€μ μ€λ₯κ° λ°μν©λλ€.
[μ€μ¬] λμμ΄ μλμΌλ‘ μ²λ¦¬λκΈ° λλ¬Έμ μλ μ΄λ²€νΈ μμ κΈ° λ΄λΆμμ Defaultλ₯Ό λ°©μ§ν μ μμ΅λλ€. https://www.chromestatus.com/features/5093566007214080 μ°Έμ‘°
μλ μ΄λ²€νΈλ Googleμ " κ°μ "μΌλ‘ μΈν΄ Chrome 56μμ κΈ°λ³Έμ΄ λμκ³ μ΄λ»κ²λ μΉμ κΉ¨λ¨ λ Έμ§λ§ μ€ν¬λ‘€ μλλ λΉ¨λΌμ‘μ΅λλ€.
iOS 11.3μ Safariλ κΈ°λ³Έμ μΌλ‘ μλμΌλ‘ μ€μ λμ΄ μκ³ touch-action:none
μ κΈ°μ‘΄ ν΄κ²° λ°©λ²μ΄ μ§μλμ§ μκΈ° λλ¬Έμ μ΄κ²μ λ λ§μ λ¬Έμ κ° λκ³
μνμ²λΌ μλνλ μ¬μ©μ μ μ μ°Έμ‘° νΈλ€λ¬λ₯Ό μμ±ν μ μλ RFC https://github.com/reactjs/rfcs/pull/28 μ μ μνμ΅λλ€(μ¦, onClick
μ κ°μ μμ±μ μ¬μ©νμ§λ§ λμ κ³μ° μμ± κ΅¬λ¬Έκ³Ό νΈλ€λ¬λ ref λ° prop κ° μ 보 λ° μ
λ°μ΄νΈλ₯Ό κ°μ Έμ΅λλ€. μ΄κ²λ€μ λΉμ μ΄ κ°μ§κ³ μλ κ±°μ λͺ¨λ κ³ κΈ μ¬μ© μ¬λ‘λ₯Ό μν λΌμ΄λΈλ¬λ¦¬λ₯Ό λ§λλ λ° μ¬μ©ν μ μμ΅λλ€.
import {onScroll} from 'react-passive-events';
<div [onScroll]={scrollHandler} />
λλ μ΄κ²μ΄ λͺ¨λ μ΄λ²€νΈλ₯Ό λ±λ‘ νλ λ°©λ²μ΄ λμ΄μΌ νλ€κ³ μκ°νμ§ μμ΅λλ€.
κ·Έλ¬λ κ°λ₯ν λͺ¨λ μ νμ μ΄λ²€νΈ(μΊ‘μ², μλ λ±...) λ±λ‘μ μ²λ¦¬νλ 볡μ‘ν λ°©λ²μ μ°Ύλ λμ λλΆλΆμ μ΄λ²€νΈ(μλ λλ λΉμλ)μ λν΄ κΈ°λ³Έ λμμ΄ λ¬΄μμΈμ§ κ²°μ νκ³ μ΄λ¬ν λ±λ‘λ μ΄λ²€νΈλ₯Ό μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. κ³ κΈ μ¬μ© μ¬λ‘λ₯Ό μ²λ¦¬νλ μν.
λͺ¨λ ν°μΉ μ΄λ²€νΈλ μ΄μ iOS 11.3μμ κΈ°λ³Έμ μΌλ‘ μλμ λλ€. λ°λΌμ λͺ¨λ ν°μΉ μ΄λ²€νΈ νΈλ€λ¬μμ event.preventDefault()λ₯Ό νΈμΆνλ κ²μ μ΄μ ν¨κ³Όκ° μμ΅λλ€.
λΉμλ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό κ°μ ν μ μμΌλ©΄ iOS 11.3 λ³κ²½ μ¬νμ ν΄κ²°νλ λ° μ΄λ €μμ κ²ͺκ³ μμ΅λλ€. https://github.com/atlassian/react-beautiful-dnd/issues/413
Vue.jsκ° μ΄κ²μ μ΄λ»κ² μ²λ¦¬νλμ§ μκ² λμκ³ κ·Έλ€μ μ κ·Ό λ°©μμ΄ μλΉν λ§μμ λλλ€.
<!-- the click event's propagation will be stopped -->
<a v-on:click.stop="doThis"></a>
<!-- the submit event will no longer reload the page -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- modifiers can be chained -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- just the modifier -->
<form v-on:submit.prevent></form>
μμ μ λͺ©λ‘μ λ€μκ³Ό κ°μ΅λλ€.
μνλ λ°©μμΌλ‘ μ΄λ²€νΈλ₯Ό ꡬμ±ν μ μμ΅λλ€. μλ§λ μ΄κ²μ μ΄ λ¬Έμ μ λν μκ°μΌλ‘ λμμ΄ λ μ μμ΅λλ€.
@KeitIG μ λ Reactμ©μΌλ‘ μ€κ³λ Vue-loader ν¬ν¬λ₯Ό μμ μ€μ λλ€.
μ΄κ±΄ λ μ¬νκ² ν΄
μ΄κ²μ΄ μ΄λ―Έ μ μλμλμ§, μλλ©΄ λ μ΄μΈμ λ€λ₯Έ μ¬λμκ² μλ―Έκ° μλμ§ νμ€νμ§ μμ΅λλ€.
onTouchStart={listener}
μκ²
onTouchStart={listener, options}
λ λ€ { passive, true, once: true }
μ κ°μ μ λ¬ μ΅μ
μ μμ°μ€λ¬μ΄ λ°©λ²μΌλ‘ λ§λ€κ³ addEventListener μ€ν€λ§μλ μΌμΉν©λλ€.
@phaistonian μ μ μμΌλ‘ μ΄λνλ©΄ νμ¬ μ‘΄μ¬νλ onEventNameCapture
νΈλ€λ¬κ° νμνμ§ μμ΅λλ€.
@alexreardon μΌλ° jsμμ listener, options
λ options
νκ°λλ ννμμ΄λ―λ‘ μμ ꡬμ±μ μλν κ²μ΄ μλκΈ° λλ¬Έμ μ΅μ
μ΄ μλλΌκ³ μκ°ν©λλ€. μ΄λ₯Ό μν΄μλ jsxκ° jsλ‘ μ»΄νμΌλλ λ°©μμ λ³κ²½ν΄μΌ νλ©° μ΄λ νκΈ°μ μΈ λ³κ²½μ΄ λ κ²μ
λλ€. λλ λμνμ΄ μ΄ κΈΈμ κ° κ²μΈμ§ μμ¬μ€λ½λ€.
μ견?
ννμ΄λΌλ μ μμ κ°μ μλλ‘ λ€λ₯΄κ² ν μ μλ€.
μλ§λ λ§μ μ΅μ μ΄ μμ κ²μ λλ€. μ΅μ μλ λ€μμ΄ ν¬ν¨λ©λλ€.
import {handler} from 'React';
onTouchStart={handler(listener, options)}
onTouchStart={{listener, options}}
λλ
onTouchStart={[listener, options]}
λλ
onTouchStart={listener} onTouchStartOptions={options}
κ·Έλλ κ°μ²΄λ₯Ό μ λ¬νλ€λ μμ΄λμ΄κ° κ°μ₯ λ§μμ λλλ€. μ΄λ μͺ½μ΄λ , μ΄κ²μ ν΄κ²°μ± μ΄ νμν©λλ€.
μμ§ ν΄κ²°μ± μ΄ μμ΅λκΉ?
μμ§ ν΄κ²°μ± μ΄ μμ΅λκΉ?
κ°κ° componentDidMount
λ° componentWillUnmount
addEventListener
λ° removeEventListener
componentWillUnmount
μ
λλ€.
κ·Έλ, μ§μ¦λλ€.
κ·Έλ¦¬κ³ νν¬λ₯Ό μ¬μ©νμ¬ ν΄κ²°νλ λ°©λ²μ λν΄ μ΄λ»κ² μκ°νμλκΉ?
...
const onClickPassive = useEventListener((e) =>Β {
console.log('passive event')
}, { passive: true })
return (
<button onClick={onClickPassive}>Click me</button>
)
@ara4n νν¬λ₯Ό μ¬μ©νλ κ²μ μ’μ§λ§ νν¬κ° μλ λ°μμ λν κ³ μ μ μΈ μ루μ μ΄ μ¬μ ν νμν©λλ€.
@sebmarkbage μ΄μ λν μ λ°μ΄νΈκ° μμ΅λκΉ? Chromeμ΄ λ°©κΈ λ°°μ‘νκ³ μ±μ΄ μμλμμ΅λλ€.
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/6662647093133312
onWheel
μ΄λ²€νΈ 리μ€λμμ κΈ°λ³Έ μ€ν¬λ‘€ λμμ μ°¨λ¨νλ €κ³ νλ©΄ λ€μ λ°μν©λλ€.
@kychanbi μ μ λμΌνμ§λ§ Windows ν¬λ‘¬μμλ§μ΄ μ€λ₯κ° λ°μν©λλ€.
@kychanbi μ€, λ¬Έμ μμ€ Wheel/Mousewheel μ΄λ²€νΈ 리μ€λλ₯Ό μλμΌλ‘ μ²λ¦¬νλ Chrome 73μ κΈ°λ₯μ λλ€.
κ΅¬μ± μμ 컨ν μ΄λ div touch-action: μμμμ css μμ±μ μ¬μ©ν μ μμ΅λλ€.
.컨ν
μ΄λ {
ν°μΉ μ‘μ
: μμ;
}
@madcher λ§μ°μ€ μ΄λ²€νΈμλ μλνμ§ μλ κ² κ°μ΅λλ€.
λ§μΉ¨λ΄ λ€μ΄ν°λΈ μλ° μ€ν¬λ¦½νΈλ₯Ό μ¬μ©νμ¬ ν΄κ²°νμ΅λλ€.
element.addEventListener("wheel", eventHandler);
μ΄ λ¬Έμ μ μ§λ©΄ν μ¬λλ€μ λκΈ° μν μμ μ€λν«:
import React, { useRef, useEffect } from 'react'
const BlockPageScroll = ({ children }) => {
const scrollRef = useRef(null)
useEffect(() => {
const scrollEl = scrollRef.current
scrollEl.addEventListener('wheel', stopScroll)
return () => scrollEl.removeEventListener('wheel', stopScroll)
}, [])
const stopScroll = e => e.preventDefault()
return (
<div ref={scrollRef}>
{children}
</div>
)
}
const Main = () => (
<BlockPageScroll>
<div>Scrolling here will only be targeted to inner elements</div>
</BlockPageScroll>
)
@madcher
onWheel
μνμ΄ CSS touch-action: none;
μλνμ§ μμ΅λλ€. componentRef = React.createRef(null);
handleWheel = (e) => {
e.preventDefault();
}
render() {
<Container style={{ touchAction: 'none' }} onWheel={this.handleWheel}>
...
</Container>
}
μ¬μ ν μ΄ μ€λ₯κ° λ°μν©λλ€.
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See <URL>
@markpradhan μ νν¬ μ루μ μ λν λμμΌλ‘ μ¬μ ν ꡬμ κ΅¬μ± μμλ₯Ό μ¬μ©νλ κ²½μ° λ€μμ μνν μ μμ΅λλ€.
componentRef = React.createRef();
handleWheel = (e) => {
e.preventDefault();
}
componentDidMount() {
if (this.componentRef.current) {
this.componentRef.current.addEventListener('wheel', this.handleWheel);
}
}
componentWillUnmount() {
if (this.componentRef.current) {
this.componentRef.current.removeEventListener('wheel', this.handleWheel);
}
}
render() {
<Container ref={this.componentRef}>...</Container>
}
@Fonger μλ§λ # 14856 λλ¬ΈμΌ κ²μ λλ€.
μ΄μ κ°μ κ²μ΄ μ μλμμ§λ§ μ¬κΈ°μ λͺ κ°μ§ μμ΄λμ΄κ° λ μμ΅λλ€.
function MyComponent() {
function onScroll(event) { /* ... */ }
onScroll.options = {capture, passive, ...};
return <div onScroll={onScroll} />;
}
μ΄λ₯Ό ν΅ν΄ μ£Όμ λ³κ²½ μμ΄ μλ μ΄λ²€νΈλ₯Ό μ½κ² μ ννκ±°λ μ΄λ²€νΈλ₯Ό μΊ‘μ²ν μ μμ΅λλ€. κ·Έλ¬λ μ λ κΈ°λ³Έμ μΌλ‘ μλ μ΄λ²€νΈ 리μ€λλΌλ μμ΄λμ΄μ ν₯λ―Έλ₯Ό λκΌμ΅λλ€. λλ preventDefaultκ° μμ μμμ Reactκ° μ€νλλ κ²μ μ°¨λ¨νλ μ£Όμ μ₯μ λ¬Ό(무μ보λ€λ)μ΄μλ κ²μ κΈ°μ΅ν©λλ€.
js
function MyComponent() {
function onScroll(event) { /* ... */ }
onScroll.shouldPreventDefault = (event): boolean => {
// some logic to decide if preventDefault() should be called.
}
onScroll.shouldStopPropagation = (event): boolean => {
// some logic to decide if stopPropagation() should be called.
}
return <div onScroll={onScroll} />;
}
μ΄κ²μ΄ μ£Όμ λ³κ²½ μ¬νμ΄ λμ§ μλλ‘ λ³΄μ₯νκΈ°λ μ΄λ €μΈ κ²μ΄μ§λ§, μ΄κ²μ΄ μνλλ€λ©΄ μ΄λ²€νΈκ° preventDefault
μ΄μ΄μΌ νλμ§ κ²°μ νλ λͺ¨λ μ½λκ° μ½λμμ 격리λκ³ Reactλ λ€μμ μνν μ μμ΅λλ€. λ©μΈ μ€λ λμμ ν΄λΉ λΆλΆλ§ μ€ννκ³ λλ¨Έμ§λ λ³λμ μμ
μ λλ λΉλκΈ°μμΌλ‘ μ€νν©λλ€.
μ΄ λ¬Έμ κ° ν΄κ²°λ λκΉμ§ event.preventDefault()
λν μ°Έμ‘°κ° λ¬Έμμμ μ κ±°λκ±°λ Chromeμ΄ μλ μ΄λ²€νΈμμ preventDefault
λ₯Ό ν μ μλ€λ κ²½κ³ κ° νμλλ κ²μ΄ κ°μ₯ μ’μ΅λλ€.
React v17λΆν° μ΄λ²€νΈ μμ λ³κ²½μ μλ―Έκ° κΆκΈν©λλ€. Lighthouseμλ λΉμλ μ΄λ²€νΈμ λν΄ ν μ€νΈνλ https://web.dev/uses-passive-event-listeners/ κ·μΉμ΄ μμ΅λλ€.
μ΄μ μλ <div onTouchStart />
κ° λ¬Έμμ λ±λ‘ λμμΌλ©° κΈ°λ³Έμ μΌλ‘ μλ μ
λλ€. κ·Έλ¬λ React v17μμλ μ΄λ²€νΈκ° React νΈλ¦¬μ 루νΈμ λ±λ‘λλ―λ‘ νΉλ³ν μμ²νμ§ μμΌλ©΄ λ μ΄μ μλμ μ΄μ§ μμ΅λλ€.
볡μ : https://codesandbox.io/s/material-demo-forked-e2u72?file=/demo.js , λΌμ΄λΈ: https://csb-e2u72.netlify.app/
μ. μ°λ €λλ κ² κ°μ΅λλ€. μλ‘μ΄ λ¬Έμ λ₯Ό μ κΈ°νκ² μ΅λλ€.
React 17 ν λ‘ μ μν΄ https://github.com/facebook/react/issues/19651 μ μ μΆνμ΅λλ€.
κ°μ₯ μ μ©ν λκΈ
μλ μ΄λ²€νΈ νΈλ€λ¬λ‘ λ±λ‘λ κ²½μ° μ΅μ νλ μ μλ ν μ΄λ²€νΈ μ²λ¦¬μ λν΄ ν¬λ‘¬μμ κ²½κ³ λ₯Ό λ°μμ΅λλ€. λ°λΌμ Reactμμ μ΄κ²μ κ°λ κ²μ κΉλν κ²μ λλ€!