В чем разница между вызовом компонента как функции и вызовом через JSX в React
Что проверяют
Понимаете ли вы, что JSX не вызывает компонент, а только описывает его, и кто на самом деле запускает функцию. Следом обычно спрашивают, что будет с хуками при прямом вызове и почему React.memo в этом случае не работает.
Как отвечать
<MyComponent a={1} /> компилируется в jsx(MyComponent, { a: 1 }) из react/jsx-runtime, и результат это обычный объект-элемент с полями type, props, key. Функция в этот момент не выполняется. Её вызовет React во время рендера, заведя для компонента собственный fiber в дереве.
MyComponent(props) это просто вызов функции внутри рендера родителя. Для React такого компонента не существует, есть только JSX, который он вернул. Отсюда последствия:
- хуки:
useStateиuseEffectвнутри регистрируются как хуки родителя. Вызов в условии или цикле нарушит правила хуков, и порядок сломается; - состояние: у «компонента» нет своего места в дереве, его state живёт и сбрасывается вместе с родителем,
keyна него не повесить; - оптимизации:
React.memoне участвует, функция выполняется при каждом рендере родителя; - обёртки: результат
memo()илиlazy()это объект, а не функция, вызвать его напрямую нельзя; - инструменты: в React DevTools и профайлере такого компонента не видно.
Когда прямой вызов допустим: для хелпера без хуков, который просто возвращает разметку, например renderRow(item). Но тогда называйте его с маленькой буквы, чтобы никто не принял его за компонент и не добавил туда хук.
Где спотыкаются
Повторяют, что при прямом вызове «не срабатывает жизненный цикл». Классовый компонент без new вообще не вызвать, а у функционального хуки как раз срабатывают, только принадлежат родителю.
Считают, что JSX сам вызывает функцию. Из-за этого не могут объяснить, как элемент можно создать, положить в переменную и так и не отрендерить.
Думают, что прямой вызов быстрее, потому что «нет лишнего слоя». Выигрыш копеечный, а теряются изоляция состояния и возможность пропустить рендер через memo.