Правильное использование функций стрелок в React

Я использую ReactJS с Babel и Webpack и используя ES6, а также предлагаемые поля классов для функций стрелок. Я понимаю, что функции стрелок делают вещи более эффективными с помощью не воссоздавая функции, которые каждый рендерит, подобно тому, как работает привязка в конструкторе. Тем не менее, я не уверен на 100%, если я правильно их использую. Ниже приведен упрощенный раздел моего кода в трех разных файлах.

Мой код:

Main.js

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

SecondClass.js

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

ThirdClass.js

<div onClick={()=>{this.props.onClick()}}>Previous</div>

Вопрос:

Правильно ли работает мой код выше, используя функцию стрелки? Я заметил, что для SecondClass.js я мог бы также использовать:

<ThirdClass type="prev" onClick={this.props.prevItem} />

Есть ли разница между одним или другим способом, так как я использовал функцию стрелок ES6 в моем первоначальном определении функции? Или я должен использовать синтаксис стрелки до конца моего последнего div?

Ответ 1

Я понимаю, что функции стрелок делают вещи более эффективными, а не воссоздание функций, каждое из которых похоже на то, как привязка в конструкторские работы.

Это неверно. Это зависит от того, где именно вы используете функцию Arrow. Если в методе рендеринга используется Arrow function, тогда они создают новый экземпляр everytime, который вызывается так же, как и bind. Рассмотрим этот пример

<div onClick={()=>{this.onClick()}}>Previous</div>

Здесь каждый раз, когда рендер называется анонимной функцией, и эта функция при вызове вызывает this.onClick.

Однако рассмотрим нижеприведенный случай

onClick = () => {
    console.log("Div is clicked")
}

В приведенном выше случае функция стрелки не воссоздает функцию каждый раз, но связывает контекст с компонентом React как An arrow function does not have its own this; the this value of the enclosing execution context is used. один раз при создании экземпляра класса. Это похоже на binding works is constructor. Это часть proposed class fields for arrow functions, и это не функция ES6,

Чтобы понять, что вы хотите спросить, вы должны знать, что функция получает свой контекст от того, где он вызывается. Чтобы узнать больше, this question проверьте.

В вашем случае вы использовали Arrow function для определения prevItem, и, следовательно, он получает контекст прилагаемого компонента React.

prevItem = () => {
    console.log("Div is clicked")
}

render(){
    return (
         <SecondClass prevItem={this.prevItem} />
    )
}

Теперь в своем дочернем элементе, даже если вы вызываете prevItem с любым настраиваемым контекстом, using bind or arrow function, prevItem при выполнении в родительском i.e Main.js получит контекст его включающего компонента React. И так как вы просто хотите выполнить функцию prevItem и не хотите передавать какие-либо данные этому ребенку, пишите

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

и

<div onClick={()=>{this.props.onClick()}}>Previous</div>

просто бесполезен и будет только увеличивать влияние на производительность, поскольку новые функции создаются в SecondClass и ThirdClass каждый раз. Вам просто не нужно, чтобы эти функции определялись как функция стрелок и могли просто писать

<ThirdClass type="prev" onClick={this.props.prevItem} />

и

<div onClick={this.props.onClick}>Previous</div>

так как он уже привязан к родительскому.

Теперь, даже если вам нужно передать некоторые дополнительные данные этим функциям из ThirdClass и SecondClass, вы не должны напрямую использовать Arrow function или bind in render. Взгляните на этот ответ на How to Avoid binding in Render method

Ответ 2

  Я понимаю, что функции стрелок делают вещи более эффективными, не воссоздавая функции каждый раз, когда они упоминаются

Это не верно.

Функции стрелок обрабатывают контекст this лексическим способом, где "обычные" функции делают это динамически. Я подробно описал это ключевое слово, если вам нужна дополнительная информация об этом.

В обоих ваших примерах функции встроенной стрелки вы создаете новый экземпляр функции для каждого render.
Это создаст и передаст новый экземпляр для каждого рендера

onClick={() => {}}

В третьем примере у вас есть только один экземпляр.
Это только передача ссылки на уже существующий экземпляр

onClick={this.myHandler}


Что касается преимуществ функций стрелок как полей классов (есть небольшая нижняя сторона, я опубликую это в нижней части ответа), если у вас есть обычный обработчик функций, которому нужен доступ к текущему экземпляру class через this:

myHandler(){
  //  this.setState(...)
}

Вам нужно будет явно указать bind его class.
Наиболее распространенным подходом будет сделать это в constructor, потому что он запускается только один раз:

constructor(props){
  super(props);
  this.myHandler = this.myHandler.bind(this);
}

Если вы используете функцию стрелки в качестве обработчика, вам не нужно bind передавать ее в class, потому что, как упоминалось выше, функция стрелки использует лексический контекст для this:

myHandler = () => {
  //  this.setState(...)
}

При обоих подходах вы будете использовать такой обработчик:

<div onClick={this.myHandler}></div> 

Основная причина такого подхода:

<div onClick={() => this.myHandler(someParameter)}></div>

Если вы хотите передать параметры обработчику рядом с нативным event, который передается, то есть вы хотите передать параметр вверх.

Как уже упоминалось, это создаст новый экземпляр функции при каждом рендеринге.
(Есть лучший подход для этого, продолжайте читать).

Пример выполнения для такого варианта использования:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      items: [{ name: 'item 1', active: false }, { name: 'item 2', active: true }],
    }
  }
  toggleITem = (itemName) => {
    this.setState(prev => {
      const nextState = prev.items.map(item => {
        if (item.name !== itemName) return item;
        return {
          ...item,
          active: !item.active
        }
      });
      return { items: nextState };
    });
  }
  render() {
    const { items } = this.state;
    return (
      <div>
        {
          items.map(item => {
            const style = { color: item.active ? 'green' : 'red' };
            return (
              <div
                onClick={() => this.toggleITem(item.name)}
                style={style}
              >
                {item.name}
              </div>
          
          )})
        }
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Ответ 3

Итак, ваш первый подход

<ThirdClass type="prev" onClick={()=>this.props.prevItem()} />

В этом вы можете передать любые аргументы, доступные в ThirdClass, функции prevItem. Это хороший способ вызова родительских функций с аргументами. Подобно этому

<ThirdClass type="prev" onClick={()=>this.props.prevItem(firstArgument, secondArgument)} />

Ваш второй подход

<ThirdClass type="prev" onClick={this.props.prevItem} />

Этот подход запрещает вам передавать любые специальные аргументы ThirdClass.

Оба apporaches правильные, это просто, это зависит от вашего использования дело. Оба подхода, использующие функцию стрелки es6, являются правильными в вышеупомянутых соответствующие сценарии

Ответ 4

Используя объявление функции JavaScript curring, можно другим способом найти другие ответы, обратите внимание на следующие коды:

clickHandler = someData => e => this.setState({
  stateKey: someData
});

Теперь в JSX вы можете написать:

<div onClick={this.clickHandler('someData')} />

clickHandler с someData возвращает функцию с аргументом e но она не используется внутри функции clickHandler. так что работает хорошо.

Для более полной записи напишите как ниже:

clickHandler = someData => () => this.setState({
  stateKey: someData
});

Не нужно e, так почему я должен писать.

Ответ 5

Использование стрелок в исходном определении функции позволяет не связывать функцию в вашем конструкторе.

Если вы не использовали стрелку...

prevItem(){
  console.log("Div is clicked")
}

Тогда вам нужно будет создать конструктор, связав его там...

class MyComponent extends Component {
  constructor(props) {
    super(props)
    this.prevItem = this.prevItem.bind(this)
  }

  prevItem() { ... }
}

Использование стрелки проще, когда вы начинаете, потому что оно просто работает, и вам не нужно понимать, что такое конструктор, и вникать в сложности this в javascript.

Однако с точки зрения производительности лучше связывать в конструкторе. Метод bind in constructor будет создавать один экземпляр функции и повторно использовать ее, даже если метод визуализации вызывается несколько раз.