React.Component를 wrapping해서 모든 ClassComponent가 상속 받게 한 후 wrapping한 클래스에 decorater를 붙여 모든 메소드에 decorater를 붙입니다.
성능 이슈가 발생할 수 있습니다.
번외
classA{
val =1;method1(){
console.log(this.val,'사용가능');}}A.prototype.method2=function(){
console.log(this.val,'사용불가능');};A.method1();// Uncaught TypeError: a.method1 is not a functionconstB=newA();B.method1();A.prototype.method2();
arrow function을 선호했으나 상속구조를 사용하기 위해서 case1을 선호하게 되었습니다.
case3
this의 binding 해야 하는 수고로움을 덜 수 있습니다.
상속을 해야하거나 접근 제어자(protected, private)가 필요할 때 method를 사용하여 역할을 좀 더 명확히 구분 하는 용도로 사용할 수 있을 것 같습니다.
readonly를 쓰면 좋겠지만, 덮어쓰는게 실수일거 같아서 굳이 필요할지는 잘 모르겠습니다…
다른 코드에 영향이 적었으면 좋겠습니다(빠드릴수 있는 사항은 최대한 없애는 것). method에 데코레이터를 쓴다면 baseComponent에서 하고 싶지만(새로운 컴포넌트 생성 시에는 고려하지 않아도 되게끔) 성능저하가 우려됩니다
constructor에서 bind한다거나
다른 함수를 통해 변화가 일어난다는 등의 일
하나의 디자인 패턴을 억지로 고수하는 일
arrow function을 통해 this의 bind 문제를 피하는 것이 새롭지 않다고 생각됩니다(class component에서 흔히 사용되는 패턴이라 학습 곡선이 낮을 것으로 추정됩니다. 또한 리액트 뿐만 JS 자체의 문법이라고 생각하기 때문에 받아 들이는데에 있어 거부감이 적을거 같습니다)
this의 유무가 아닌 상속에 유무에 따라서 하고 싶습니다. -> arrow로 했다가 상속이 필요해서 method로 바꾸는 방식은 하위를 위해 상위를 고치는 행위로 위험할 수 있습니다.
case1
import React from'react';exportclassAppextendsReact.Component{
state ={a:{b:1,},};render(){return(<div><Base/><A/><B/><C/><D/></div>);}}exportdefault App;classBaseextendsReact.Component{
state ={a:1,b:2,c:3,};method1=()=>{
console.log('Base.metho1',this.state.a);this.method2();};method2(){
console.log('Base.metho2',this.state.b);}render(){return<divonClick={this.method1}>Base</div>;}}classAextendsBase{
state ={d:4,e:5,};method2(){super.method2();
console.log('A.metho2',this.state.d);}render(){return<divonClick={this.method1}>A</div>;}}classBextendsBase{
state ={f:6,g:7,};method2(){
console.log('B.metho2',this.state.f);}render(){return<divonClick={this.method1}>B</div>;}}classCextendsBase{
state ={h:8,i:9,};method1=()=>{
console.log('C.method1',this.state.h);};render(){return<divonClick={this.method1}>C</div>;}}classDextendsBase{
state ={j:10,k:11,};method2=()=>{
console.log('D.method1',this.state.j);};render(){return<divonClick={this.method1}>D</div>;}}
위의 방식으로 해결해 보려고 했으나 상속 받은 method1의 this.state는 undefined를 리턴할 뿐입니다.
import React from'react';exportclassAppextendsReact.Component{
state ={a:{b:1,},};render(){return(<div><Base/><A/><B/><C/><D/></div>);}}exportdefault App;classBaseextendsReact.Component{
state ={a:1,b:2,c:3,};method1=()=>{
console.log('Base.metho1',this.state.a);this.method2();};method2(){
console.log('Base.metho2',this.state.b);}render(){return<divonClick={this.method1}>Base</div>;}}classAextendsBase{
state ={a:1,b:2,d:4,e:5,};method2(){super.method2();
console.log('A.metho2',this.state.d);}render(){return<divonClick={this.method1}>A</div>;}}classBextendsBase{
state ={a:1,b:2,f:6,g:7,};method2(){
console.log('B.metho2',this.state.f);}render(){return<divonClick={this.method1}>B</div>;}}classCextendsBase{
state ={a:1,b:2,h:8,i:9,};method1=()=>{
console.log('C.method1',this.state.h);};render(){return<divonClick={this.method1}>C</div>;}}classDextendsBase{
state ={a:1,b:2,j:10,k:11,};method2=()=>{
console.log('D.method1',this.state.j);};render(){return<divonClick={this.method1}>D</div>;}}
위와 같이 A에 state 값을 넣어주면 정상 동작합니다…
arrow function으로 선언한 문제인거 같았으나 method로 선언해도 같습니다…
this.state가 있기 때문에 더 이상 위로 갈 필요가 없기 때문에 발생하는 문제인거 같습니다… 당연히 a,b가 있는게 맞는거 같습니다… typescript에서는 interface로 필요 state를 정의할 수 있습니다.
arrow로 쓰나 method로 쓰나 같다고 한다면 method로 통일하는 것이 좋을 거 같습니다.
element 핸드러에 함수를 축약형 arrow로 지정하고 그 함수를 메소드로 빼 낸다면 문제는 해결 될 거 같습니다.
method binding도 arrow function도 this의 bind 문제를 해결하기 때문에 이를 해결하기 위해서는 arrow function이 가장 적합해 보입니다.
클래스 컴포넌트인만큼 모든 함수를 메소드 형태로 쓰고 return 안의 handler의 경우에만 arrow function을 사용하는 것을 추구하고 있습니다.
Airbnb 같은 경우는 Airbnb React/JSX Style Guide에서 method 부분을 보면 알 수 있듯, field function을 very bad라 할 정도로 사용하는 것을 지양하고 있습니다.
Why? A bind call in the render path creates a brand new function on every single render. Do not use arrow functions in class fields, because it makes them challenging to test and debug, and can negatively impact performance, and because conceptually, class fields are for data, not logic.
// badclassextends React.Component {onClickDiv(){// do stuff}render(){return<div onClick={this.onClickDiv.bind(this)}/>;}}// very badclassextends React.Component {onClickDiv=()=>{// do stuff}render(){return<div onClick={this.onClickDiv}/>}}// goodclassextends React.Component {constructor(props){super(props);this.onClickDiv =this.onClickDiv.bind(this);}onClickDiv(){// do stuff}render(){return<div onClick={this.onClickDiv}/>;}}