Правильное использование функций стрелки в 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>
вопрос:
мой код выше, используя функции стрелки правильно? Я заметил это для второго класса.js я мог бы также использовать:
<ThirdClass type="prev" onClick={this.props.prevItem} />
есть ли разница между одним методом или другим, так как я использовал функцию стрелки ES6 в моем исходном определении функции? Или я должен использовать синтаксис стрелки весь путь до моего последнего div?
4 ответов
я понимаю, что функции стрелки делают вещи более эффективными, не воссоздание функций каждый рендер похож на то, как привязка в конструктор работает.
это неправда. Это зависит от того, где именно вы используете функцию стрелки. Если Arrow function
используются в методе рендеринга, затем они создают новый экземпляр everytime
рендер называется так же, как how 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.
один раз при создании экземпляра класса. Это похоже на how 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>
так как его уже связали в родитель.
теперь, даже если вам нужно передать некоторые дополнительные данные этой функции из третьего и второго классов, вы не должны напрямую использовать Arrow function
или bind in render
. Взгляните на этот ответ на How to Avoid binding in Render method
я понимаю, что функции стрелки делают вещи более эффективными, не воссоздание функций каждый раз, когда они упоминаются
функции стрелки обрабатывает this
контекст лексическим способом, где" нормальная " функция делает это динамически.
в обоих примерах функции встроенной стрелки вы создаете новый экземпляр функции на каждый render
.
Это создаст и передаст новый экземпляр на каждом render
onClick={() => {}}
в 3-м примере у вас есть только один экземпляр.
Это только передает ссылку на уже существующий экземпляр
onClick={this.myHandler}
Что касается преимуществ функций стрелки как полей класса (есть небольшой недостаток, я опубликую его в нижней части ответа), если у вас есть обычный обработчик функций, который должен получить доступ к текущему пример
class
via 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>
если вы хотите передать параметры обработчику рядом с native 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>
лучшим подходом было бы создание компонентного состава.
Вы можете создать дочерний компонент, который обертывает соответствующую разметку, будет иметь собственный обработчик и получит оба data
и handler
как реквизит от родителя.
дочерний компонент затем вызовет обработчик, который он получил от родителя, и передаст data
в качестве параметра.
пример с ребенка компонент:
class Item extends React.Component {
onClick = () => {
const { onClick, name } = this.props;
onClick(name);
}
render() {
const { name, active } = this.props;
const style = { color: active ? 'green' : 'red' };
return (<div style={style} onClick={this.onClick}>{name}</div>)
}
}
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 => {
return <Item {...item} onClick={this.toggleITem} />
})
}
</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>
класс поля вниз в сторону:
Как я уже упоминал, есть небольшая нижняя сторона для полей класса.
Разница между методом класса и полем класса заключается в том, что поле класса присоединяется к instance
на class
(функция конструктора).
где в качестве класса методы и объекты присоединяются к прототипу.
следовательно, если вы будете иметь смехотворно большое количество экземпляров этого класса мая получить хит производительности.
учитывая этот блок кода:
class MyClass {
myMethod(){}
myOtherMethod = () => {}
}
Бабель будет transpile это:
var _createClass = function() {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function(Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var MyClass = function() {
function MyClass() {
_classCallCheck(this, MyClass);
this.myOtherMethod = function() {};
}
_createClass(MyClass, [{
key: "myMethod",
value: function myMethod() {}
}]);
return MyClass;
}();
Итак, ваш первый подход
<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 аргументы.
оба приложения правы, это просто, это зависит от вашего использования случай. Оба подход с использованием функции стрелки es6 и прямо в вышеупомянутом соответствующие сценарии
использование стрелок в исходном определении функции позволяет не связывать функцию в конструкторе.
Если вы не используете стрелу...
prevItem(){
console.log("Div is clicked")
}
тогда вам придется создать конструктор, привязать его там...
class MyComponent extends Component {
constructor(props) {
super(props)
this.prevItem = this.prevItem.bind(this)
}
prevItem() { ... }
}
использование стрелки проще, когда вы начинаете, потому что она просто работает, и вам не нужно понимать, что такое конструктор, и углубляться в сложности this
в javascript.