Как решить ошибку jsx-no-bind на функциональном компоненте?

У меня есть небольшой компонент для изменения языка моего сайта. Поскольку eslint настроен с помощью jsx-no-bind, он вызывает ошибку в приведенном ниже коде.

const ChangeLanguage = ({ toggleLanguage }) => {

    const toggle = () => {
        console.info('hi')
        toggleLanguage()
    }

    return (
        <IconButton
            onClick = {toggle}
        >
            <Language /> // this is an svg-icon
        </IconButton>
    )
}

export default connect(null, { toggleLanguage })(ChangeLanguage)

Я погуглил использование jsx-no-bind. Кто-то сказал, что мы должны включить его, так как при каждом рендеринге javascript генерирует новую функцию, и это вызывает ненужный рендеринг (например, соглашение о коде airbnb[связь]). Но некоторые другие говорят, что улучшение производительности незначительно и снижает читабельность кода (например, [связь]). Вот два вопроса:

  1. Как решить ошибку(т.е. не отключая jsx-no-bind).
  2. Что вы предлагаете по поводу конфигурации jsx-no-bind (т. е. я должен удалить ее полностью/частично из конфигурации eslint или нет?).

PS: В Интернете есть дополнительная документация о передаче стрелочных функций или методов привязки в качестве реквизита. Но я упомянул только два из них в постановке вопроса.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
5
0
2 637
3

Ответы 3

Это происходит из-за того, что вы передали функцию в операторе return вашего компонента, и это создает новую функцию каждый раз, когда компонент перерисовывается.

чтобы избежать этого, вы можете использовать только его ссылку. так,

const ChangeLanguage = ({ toggleLanguage }) => {
return (
    <IconButton
        onClick = {toggleLanguage} // <----->
        <Language /> // this is an svg-icon
    </IconButton>
 )
}

export default connect(null, { toggleLanguage })(ChangeLanguage)

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

Bonje Fir 07.03.2019 11:59

ну, я лично не сталкивался с ситуацией, когда вы «должны» создать функцию в операторе возврата, вы можете определить функцию вне возврата или метода рендеринга и передать ее ссылку внутри. ИЛИ, если всякая надежда потеряна, вы можете просто отключить правило eslint для этого файла, написав комментарий вида: та же строка: // eslint-disable-line jsx-no-bind следующая строка: // eslint-disable-next-line jsx-no-bind весь код ниже: /* eslint-disable jsx-no-bind */

Amr 07.03.2019 12:06

Я немного меняю код, не меняя исходной задачи. Предположим, мы также должны/хотим сделать что-то еще (например, вывести строку на консоль)... . Проблема все еще есть!

Bonje Fir 07.03.2019 12:11

Я полагаю, потому что это все еще внутри рендеринга, поскольку это функциональный компонент, если вы переключите его на компонент, основанный на классе, вы можете определить функцию вне метода рендеринга, и это будет работать. Я не уверен, обходит ли useCallback() хук это правило или нет, предлагаю вам попробовать/поискать.

Amr 07.03.2019 15:15

Спасибо за ваше предложение о крючке useCallback, я посмотрю на него. Но для первой части (поскольку лучше определить функциональный компонент, а не основанный на классе) теперь лучше определить класс, который подчиняется правилу jsx-no-bind, или функциональный компонент, который не подчиняется?!

Bonje Fir 07.03.2019 16:45

eslint не жалуется, если мы делаем:

const ChangeLanguage = ({ toggleLanguage }) => {

    function toggle () {
        console.info('hi')
        toggleLanguage()
    }

    return (
        <IconButton
            onClick = {toggle}
        >
            <Language /> // this is an svg-icon
        </IconButton>
    )
}

export default connect(null, { toggleLanguage })(ChangeLanguage)

Но я не уверен, в чем разница. Реакция в этом случае больше не создает функцию при каждом рендеринге или просто правило не распознает ее?

Решение задокументировано здесь: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md#react-hooks

Functional components are often used alongside hooks, and the most trivial case would occur if your callback is completely independent from your state. In this case, the solution is as simple as moving the callback out of your component:

const onClick = () => {
  console.info("Independent callback");
};
const Button = () => {
  return (
    <button type = "button" onClick = {onClick}>Label</button>
  );
};

Otherwise, the idiomatic way to avoid redefining callbacks on every render would be to memoize them using the useCallback hook:

const Button = () => {
  const [text, setText] = useState("Before click");
  const onClick = useCallback(() => {
    setText("After click");
  }, [setText]); // Array of dependencies for which the memoization should update
  return (
    <button type = "button" onClick = {onClick}>{text}</button>
  );
};

Другие вопросы по теме