programing

왜 JSX 소품은 화살표 기능이나 바인드를 사용하면 안 되는가?

goodsources 2023. 3. 27. 21:12
반응형

왜 JSX 소품은 화살표 기능이나 바인드를 사용하면 안 되는가?

리액트 앱에서 보풀이 발생하고 있는데 다음 오류가 나타납니다.

error    JSX props should not use arrow functions        react/jsx-no-bind

기능)을 실행하고 onClick

{this.state.photos.map(tile => (
  <span key={tile.img}>
    <Checkbox
      defaultChecked={tile.checked}
      onCheck={() => this.selectPicture(tile)}
      style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
    />
    <GridTile
      title={tile.title}
      subtitle={<span>by <b>{tile.author}</b></span>}
      actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
    >
      <img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
    </GridTile>
  </span>
))}

이것이 피해야 할 나쁜 관행인가요?그리고 그것을 하는 가장 좋은 방법은 무엇일까요?

JSX 소품에서 인라인 화살표 기능을 사용하면 안 되는 이유

JSX에서 화살표 함수 또는 바인딩을 사용하면 각 렌더링에서 함수가 재생성되므로 성능이 저하됩니다.

  1. 함수가 생성될 때마다 이전 함수는 가비지 수집됩니다.많은 요소를 다시 렌더링하면 애니메이션에 문제가 발생할 수 있습니다.

  2. 하면, 「」의 원인이 .PureComponent「」를 하는 shallowCompare shouldComponentUpdate이치노화살표 기능 소품은 매번 재생성되므로 얕은 비교를 통해 소품 변경으로 식별되고 구성 요소가 다시 렌더링됩니다.

다음 두 가지 예에서 볼 수 있듯이 인라인 화살표 기능을 사용하면<Button>컴포넌트는 매번 재렌더됩니다(콘솔에 '버튼' 텍스트가 표시됩니다).

예 1 - 인라인핸들러가 없는 Pure Component

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  onClick = () => this.setState((prevState) => ({
    counter: prevState.counter + 1
  }));
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ this.onClick } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

예 2 - 인라인핸들러를 사용한 Pure Component

class Button extends React.PureComponent {
  render() {
    const { onClick } = this.props;
    
    console.log('render button');
    
    return (
      <button onClick={ onClick }>Click</button>
    );
  }
}

class Parent extends React.Component {
  state = {
    counter: 0
  }
  
  render() {
    const { counter } = this.state;
    
    return (
      <div>
        <Button onClick={ () => this.setState((prevState) => ({
          counter: prevState.counter + 1
        })) } />
        <div>{ counter }</div>
      </div>
    );
  }
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>

의 바인딩 this 없음

  1. 생성자에서 메서드를 수동으로 바인딩:

    class Button extends React.Component {
      constructor(props, context) {
        super(props, context);
    
        this.cb = this.cb.bind(this);
      }
    
      cb() {
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    
  2. 화살표 함수를 사용하여 proposal-class-fields를 사용한 메서드의 바인딩.이것은 스테이지 3 프로포절이기 때문에 스테이지 3 프리셋 또는 클래스 속성 변환을 babel 설정에 추가해야 합니다.

    class Button extends React.Component {
      cb = () => { // the class property is initialized with an arrow function that binds this to the class
    
      }
    
      render() {
        return (
          <button onClick={ this.cb }>Click</button>
        );
      }
    }
    

내부 콜백을 가진 함수 컴포넌트

함수 컴포넌트 내에 내부 함수(이벤트 핸들러 등)를 작성하면 컴포넌트가 렌더링될 때마다 함수가 재생성됩니다.콘텍스트를 시켜) 자 컴포넌트 「」 「」 「」 「」)에.Button이 경우), 그 아이도 재입학합니다.

예 1 - 내부 콜백을 사용하는 기능 컴포넌트:

const { memo, useState } = React;

const Button = memo(({ onClick }) => console.log('render button') || (
  <button onClick={onClick}>Click</button>
));

const Parent = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

이 문제를 해결하려면 콜백을 후크로 랩하여 의존관계를 빈 배열로 설정합니다.

주의:useState생성된 함수는 현재 상태를 제공하는 업데이트 함수를 받아들입니다.해서 지금 를 ''로 할 필요는 useCallback.

예 2 - useCallback으로 둘러싸인 내부 콜백을 가진 함수 컴포넌트:

const { memo, useState, useCallback } = React;

const Button = memo(({ onClick }) => console.log('render button') || (
  <button onClick={onClick}>Click</button>
));

const Parent = () => {
  const [counter, setCounter] = useState(0);
  
  const increment = useCallback(() => setCounter(counter => counter + 1), []);
  
  return (
    <div>
      <Button onClick={increment} />
      
      <div>{counter}</div>
    </div>
  );
}

ReactDOM.render(
  <Parent />,
  document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

이와 같이 인라인 기능을 사용하는 것은 문제 없습니다.보풀 제거 규칙이 오래되었습니다.

이 규칙은 화살표 기능이 일반적이지 않았던 시절부터 사용 속도가 느리던 .bind(이것)를 사용한 것입니다.Chrome 49에서 성능 문제가 수정되었습니다.

인라인 기능을 소품으로 하위 구성요소에 전달하지 않도록 주의하십시오.

React Router의 저자 Ryan Florence는 이에 대한 훌륭한 글을 썼습니다.

https://reacttraining.com/blog/react-inline-functions-and-performance/

이는 화살표 함수가 JSX 속성에서 사용되는 경우 각 렌더링에 함수의 새 인스턴스를 생성하기 때문입니다.이로 인해 가비지 컬렉터에 큰 부담이 발생할 수 있으며 기능이 재사용되는 대신 폐기되므로 브라우저가 "핫 경로"를 최적화하는 데 방해가 될 수 있습니다.

자세한 내용은 https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md 에서 보실 수 있습니다.

왜 JSX 소품은 화살표 기능이나 바인드를 사용하면 안 되는가?

대부분의 경우 인라인 함수는 최적화된 컴포넌트의 메모화를 방해할 수 있습니다.

에는 React의 에 관한 우려는 각 통과시키느냐와 shouldComponentUpdate하위 구성요소의 최적화.(표준)

추가 함수 생성 비용이 더 적게 듭니다.

" " " " " "Function.prototype.bind 여기에서 수정되고 화살표 함수는 네이티브한 것이거나 babel에 의해 플레인 함수로 변환됩니다.두 경우 모두 느리지 않다고 가정할 수 있습니다.(React Training)

함수 작성에 비용이 많이 든다고 하는 사람들은 항상 잘못 알고 있다고 생각합니다(React 팀은 이런 말을 한 적이 없습니다).(트윗)

react/jsx-no-bind칙이이유?유 용??

메모된 컴포넌트가 의도대로 동작하는지 확인해야 합니다.

  • React.memo컴포넌트의 ) (기능 컴포넌트의 경우)
  • PureComponent 커스텀 "" " " " "shouldComponentUpdate컴포넌트의 ) (클래스 컴포넌트의 경우)

이 규칙을 준수함으로써 안정적인 함수 객체 참조가 전달됩니다.따라서 위의 컴포넌트는 이전 소품이 변경되지 않은 경우 재렌더를 방지하여 성능을 최적화할 수 있습니다.

ESLint 오류 해결 방법

클래스:핸들러를 메서드 또는 클래스 속성으로 정의합니다.this바인드
후크: 를 사용합니다.

미들그라운드

대부분의 경우 인라인 함수는 사용이 매우 편리하고 성능 요건 면에서도 전혀 문제가 없습니다.유감스럽게도 이 규칙은 메모된 구성 요소 유형으로만 제한될 수 없습니다.그래도 전체적으로 사용하려는 경우, 예를 들어 단순 DOM 노드에 대해 사용 불가능으로 설정할 수 있습니다.

rules: {
  "react/jsx-no-bind": [ "error", { "ignoreDOMComponents": true } ],
}

const Comp = () => <span onClick={() => console.log("Hello!")} />; // no warning

동일한 인수로 새로운 함수를 만들지 않도록 함수의 바인드 결과를 메모할 수 있습니다.다음은 라는 이름의 간단한 유틸리티입니다.memobind사용방법 : https://github.com/supnate/memobind

이 에러는 함수를 useCallback 안에 랩함으로써 제거할 수 있습니다.

콜백에서 데이터를 언제 전달해야 하는지 궁금하신 분들을 위해.예:

const list = todos.map((todo, index) => (
  <Todo
    onClick={() => { onTodoClick(todo.id, todo.title, index) }
  />
));

솔루션

공식 문서에 따르면 다음 작업을 수행해야 합니다.

  1. 함수 인수를 하위 구성 요소로 이동합니다.
const list = todos.map((todo, index) => (
  <Todo
    onClick={onTodoClick}
    todoId={todo.id}
    todoTitle={todo.title}
    indexOnList={index}
  />
));
  1. 하위 구성 요소(<Todo />)는 콜에서 인수를 전달합니다.
function Todo(props) {
  // component properties
  const { onClick, todoId, todoTitle, indexOnList } = props;

  // we move the call from the parent to the children
  const handleClick = useCallback(() => {
    onClick(todoId, todoTitle, indexOnList);
  }, [todoId, todoTitle, indexOnList]);

  return (
    <div onClick={handleClick}>
    {/* the rest of the component remains the same */}
    </div>
  );
}

이게 최선의 해결책인가요?

나는 이 해결책이 싫다.결국 하위 구성요소에 부모의 데이터와 논리가 있게 됩니다.이로 인해 하위 구성 요소가 상위 구성 요소에 종속되어 종속성 규칙을 위반하게 됩니다.

그건 절대 안 돼요.

내가 하는 일은 이 규칙을 무효로 하는 것이다.Ryan Florence (React Router 저자)에 따르면, 이것은 어쨌든 큰 문제가 아닙니다: https://medium.com/ @http:/http:/http:/http:/http:/http:/http:/http:/http:/http:/http:/http:/http:/h


새로운 리액트 튜토리얼(베타판, 2023년 1월)에서는 기능과 화살표 기능을 모두 JSX 소품으로 사용합니다.이는 이것이 주요 관심사가 아님을 강하게 시사한다.

react-cached-handler 라이브러리를 사용하여 화살표 기능을 사용할 수 있으므로 성능 재렌더링을 걱정할 필요가 없습니다.

메모: 내부적으로는 지정된 키로 화살표 기능을 캐시하므로 재렌더링에 대해 걱정할 필요가 없습니다.

render() {
    return (
        <div>
            {this.props.photos.map((photo) => (
                <Photo
                    key={photo.url}
                    onClick={this.handler(photo.url, (url) => {
                        console.log(url);
                    })}
                />
            ))}
        </div>
    );
}

기타 기능:

  • 이름 있는 핸들러
  • 화살표 기능으로 이벤트 처리
  • 키, 커스텀 인수 및 원래 이벤트에 대한 액세스
  • 컴포넌트 렌더링 퍼포먼스
  • 핸들러의 커스텀콘텍스트

또한 이 오류는 onClick 핸들러에서 사용하는 함수가 렌더 메서드 외부에서 정의된 일반(비인라인) 함수이지만function키워드를 지정합니다.

여기에 이미지 설명 입력

렌더 메서드 외부에서 const를 사용하여 핸들러 함수를 화살표 함수로 선언하면 React가 불평을 중지합니다.

여기에 이미지 설명 입력

언급URL : https://stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind

반응형