Как передать функцию в компонент react
Перейти к содержимому

Как передать функцию в компонент react

  • автор:

Компоненты и пропсы

Компоненты позволяют разбить интерфейс на независимые части, про которые легко думать в отдельности. Их можно складывать вместе и использовать несколько раз. На этой странице мы ознакомимся с самой идеей компонентов — детальное описание API находится здесь.

Во многом компоненты ведут себя как обычные функции JavaScript. Они принимают произвольные входные данные (так называемые «пропсы») и возвращают React-элементы, описывающие, что мы хотим увидеть на экране.

Функциональные и классовые компоненты

Проще всего объявить React-компонент как функцию:

function Welcome(props)  return h1>Привет, props.name>h1>; >

Эта функция — компонент, потому что она получает данные в одном объекте («пропсы») в качестве параметра и возвращает React-элемент. Мы будем называть такие компоненты «функциональными», так как они буквально являются функциями.

Ещё компоненты можно определять как классы ES6:

class Welcome extends React.Component  render()  return h1>Привет, this.props.name>h1>; > >

С точки зрения React, эти два компонента эквивалентны.

Функциональным и классовым компонентам доступны дополнительные возможности, о которых мы поговорим в следующих главах.

Как отрендерить компонент

Пока что мы только встречали React-элементы, представляющие собой DOM-теги:

const element = div />;

Но элементы могут описывать и наши собственные компоненты:

const element = Welcome name="Алиса" />;

Когда React встречает подобный элемент, он собирает все JSX-атрибуты и дочерние элементы в один объект и передаёт их нашему компоненту. Этот объект называется «пропсы» (props).

Например, этот компонент выведет «Привет, Алиса» на страницу:

function Welcome(props)  return h1>Привет, props.name>h1>; > const root = ReactDOM.createRoot(document.getElementById('root')); const element = Welcome name="Алиса" />;root.render(element);

Давайте разберём, что именно здесь происходит:

  1. Мы вызываем root.render() c React-элементом .
  2. React вызывает наш компонент Welcome с пропсами .
  3. Наш компонент Welcome возвращает элемент

    Привет, Алиса

    в качестве результата.

  4. React DOM делает минимальные изменения в DOM, чтобы получилось

    Привет, Алиса

    .

Примечание: Всегда называйте компоненты с заглавной буквы.

Если компонент начинается с маленькой буквы, React принимает его за DOM-тег. Например, это div-тег из HTML, а это уже наш компонент Welcome , который должен быть в области видимости.

Чтобы узнать больше про это соглашение, прочитайте Углублённое изучение JSX.

Компоненты могут ссылаться на другие компоненты в возвращённом ими дереве. Это позволяет нам использовать одну и ту же абстракцию — компоненты — на любом уровне нашего приложения. Неважно, пишем ли мы кнопку, форму или целый экран: все они, как правило, представляют собой компоненты в React-приложениях.

Например, компонент App может отрендерить компонент Welcome несколько раз:

function Welcome(props)  return h1>Привет, props.name>h1>; > function App()  return ( div> Welcome name="Алиса" />  Welcome name="Базилио" />  Welcome name="Буратино" />  div> ); >

В приложениях, написанных на React с нуля, как правило, есть один компонент App , который находится на самом верху. В случае, если вы переписываете существующее приложение на React, имеет смысл начать работу с маленького компонента типа Button и постепенно двигаться «вверх» по иерархии.

Не бойтесь разбивать компоненты на части.

Допустим, у нас есть компонент Comment :

function Comment(props)  return ( div className="Comment"> div className="UserInfo"> img className="Avatar" src=props.author.avatarUrl> alt=props.author.name> /> div className="UserInfo-name"> props.author.name> div> div> div className="Comment-text"> props.text> div> div className="Comment-date"> formatDate(props.date)> div> div> ); >

Этот компонент представляет собой комментарий в социальной сети. Его пропсы включают в себя author (объект), text (строка), и date (дата).

С этим компонентом может быть не очень удобно работать из-за излишней вложенности. Мы также не можем повторно использовать его составные части. Давайте извлечём из него пару компонентов.

Для начала извлечём Avatar :

function Avatar(props)  return ( img className="Avatar" src=props.user.avatarUrl> alt=props.user.name> /> ); >

Компоненту Avatar незачем знать, что он рендерится внутри Comment . Поэтому мы дали его пропу чуть менее конкретное имя — user , а не author .

Пропсы следует называть так, чтобы они имели смысл в первую очередь с точки зрения самого компонента, а уже во вторую тех компонентов, которые его рендерят.

Теперь можно немножко упростить наш Comment :

function Comment(props)  return ( div className="Comment"> div className="UserInfo"> Avatar user=props.author> />  div className="UserInfo-name"> props.author.name> div> div> div className="Comment-text"> props.text> div> div className="Comment-date"> formatDate(props.date)> div> div> ); >

Следующим шагом извлечём компонент UserInfo , который рендерит Avatar рядом с именем пользователя:

function UserInfo(props)  return ( div className="UserInfo">  Avatar user=props.user> />  div className="UserInfo-name">  props.user.name>  div>  div> ); >

Это позволит ещё сильнее упростить Comment :

function Comment(props)  return ( div className="Comment"> UserInfo user=props.author> />  div className="Comment-text"> props.text> div> div className="Comment-date"> formatDate(props.date)> div> div> ); >

Извлечение компонентов может сначала показаться неблагодарной работой. Тем не менее, в больших приложениях очень полезно иметь палитру компонентов, которые можно многократно использовать. Если вы не уверены, извлекать компонент или нет, вот простое правило. Если какая-то часть интерфейса многократно в нём повторяется ( Button , Panel , Avatar ) или сама по себе достаточно сложная ( App , FeedStory , Comment ), имеет смысл её вынести в независимый компонент.

Пропсы можно только читать

Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.

Возьмём для примера функцию sum :

function sum(a, b)  return a + b; >

Такие функции называют «чистыми», потому что они не меняют свои входные данные и предсказуемо возвращают один и тот же результат для одинаковых аргументов.

А вот пример нечистой функции — она записывает данные в свои же аргументы:

function withdraw(account, amount)  account.total -= amount; >

React достаточно гибкий, но есть одно правило, которое нельзя нарушать:

React-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.

Конечно, интерфейсы приложений обычно изменяются с течением времени. В следующей главе мы узнаем о том, что такое «состояние» компонента. Состояние даёт компонентам возможность реагировать на действия пользователя, ответы сервера и другие события, не нарушая чистоту компонента.

Передача функций в компоненты

Передавайте обработчики событий и другие функции через пропсы дочерним компонентам:

button onClick=this.handleClick>>

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

Как привязать функцию к экземпляру компонента?

В зависимости от того, какой синтаксис и подход к созданию компонентов вы используете, существует несколько способов удостовериться, что функции имеют доступ к таким атрибутам компонента, как this.props и this.state .

Привязка в конструкторе (ES2015)

class Foo extends Component  constructor(props)  super(props); this.handleClick = this.handleClick.bind(this); > handleClick()  console.log('По кнопке кликнули'); > render()  return button onClick=this.handleClick>>Нажми на меняbutton>; > >

Свойства класса (ES2022)

class Foo extends Component  handleClick = () =>  console.log('По кнопке кликнули'); >; render()  return button onClick=this.handleClick>>Нажми на меняbutton>; > >

Привязка в методе render()

class Foo extends Component  handleClick()  console.log('По кнопке кликнули'); > render()  return button onClick=this.handleClick.bind(this)>>Нажми на меняbutton>; > >

Примечание:

Использование Function.prototype.bind в render() создаёт новую функцию при каждом рендере компонента, что может повлиять на производительность (см. ниже).

Стрелочная функция в render()

class Foo extends Component  handleClick()  console.log('По кнопке кликнули'); > render()  return button onClick=() => this.handleClick()>>Нажми на меняbutton>; > >

Примечание:

Использование стрелочной функции в render() создаёт новую функцию при каждой отрисовке компонента, что может нарушать оптимизации, использующие строгое сравнение для определения идентичности.

Можно ли использовать стрелочные функции в методе render()?

В целом, да. Зачастую это самый простой способ передать параметры через колбэки.

Если же у вас возникли проблемы с производительностью — оптимизируйте!

Зачем вообще нужна привязка?

В JavaScript эти два фрагмента кода не равнозначны:

obj.method();
var method = obj.method; method();

Привязка гарантирует, что второй фрагмент будет работать так же, как и первый.

В React, как правило, привязывать нужно только те методы, которые вы хотите передать другим компонентам. Например, > передаёт this.handleClick , поэтому его нужно привязать. Впрочем, метод render и методы жизненного цикла привязывать не обязательно, так как мы не передаём их в другие компоненты.

Ознакомьтесь со статьёй Йехуды Катц, в которой более подробно объяснено, что такое привязка, и как работают функции в JavaScript.

Почему моя функция вызывается каждый раз при отрисовке компонента?

Убедитесь, что вы не вызываете функцию, когда передаёте её компоненту:

render()  // Неправильно: вместо ссылки была вызвана функция handleClick! return button onClick=this.handleClick()>>Нажми на меняbutton> >

Вместо этого передайте саму функцию (без скобок):

render()  // Правильно: handleClick передаётся как ссылка! return button onClick=this.handleClick>>Нажми на меняbutton> >

Как передать параметры обработчику событий или колбэку?

Чтобы передать параметры обработчику событий, оберните его в стрелочную функцию:

button onClick=() => this.handleClick(id)> />

Это действие равносильно использованию .bind :

button onClick=this.handleClick.bind(this, id)> />

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

const A = 65 // ASCII-код символа class Alphabet extends React.Component  constructor(props)  super(props); this.state =  justClicked: null, letters: Array.from(length: 26>, (_, i) => String.fromCharCode(A + i)) >; > handleClick(letter)  this.setState( justClicked: letter >); > render()  return ( div> Just clicked: this.state.justClicked> ul> this.state.letters.map(letter => li key=letter> onClick=() => this.handleClick(letter)>> letter> li> )> ul> div> ) > >

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

В качестве альтернативного подхода вы можете использовать DOM API, чтобы хранить необходимые для обработчиков событий данные. Рассмотрите этот подход, если вам нужно оптимизировать большое количество элементов или использовать дерево визуализации, полагающееся на компонент React.PureComponent для проверки на равенство.

const A = 65 // ASCII-код символа class Alphabet extends React.Component  constructor(props)  super(props); this.handleClick = this.handleClick.bind(this); this.state =  justClicked: null, letters: Array.from(length: 26>, (_, i) => String.fromCharCode(A + i)) >; > handleClick(e)  this.setState( justClicked: e.target.dataset.letter >); > render()  return ( div> Just clicked: this.state.justClicked> ul> this.state.letters.map(letter => li key=letter> data-letter=letter> onClick=this.handleClick>> letter> li> )> ul> div> ) > >

Как предотвратить слишком быстрый или слишком частый вызов функции?

Если вы используете обработчики событий, такие как onClick или onScroll , и хотите предотвратить быстрое срабатывание колбэков, вы можете ограничить скорость выполнения колбэка. Для этого вы можете использовать:

  • троттлинг: выборочные изменения, зависимые от частоты, основанной на времени (напр. _.throttle )
  • дебаунсинг: изменения, задействованные после некого периода бездействия (напр. _.debounce )
  • троттлинг с помощью requestAnimationFrame : выборочные изменения, основанные на requestAnimationFrame (напр. raf-schd )

Взгляните на визуализацию, которая сравнивает функции throttle и debounce .

Примечание:

_.debounce , _.throttle и raf-schd предусматривают метод cancel для отмены отложенных колбэков. Вы должны либо вызвать этот метод из componentWillUnmount, либо удостовериться, что компонент всё ещё встроен в пределах отложенной функции.

Троттлинг предотвращает повторный вызов функции в заданный период времени. Этот метод был задействован в примере ниже, чтобы не допустить вызов обработчика «click» чаще чем раз в секунду.

import throttle from 'lodash.throttle'; class LoadMoreButton extends React.Component  constructor(props)  super(props); this.handleClick = this.handleClick.bind(this); this.handleClickThrottled = throttle(this.handleClick, 1000); > componentWillUnmount()  this.handleClickThrottled.cancel(); > render()  return button onClick=this.handleClickThrottled>>Загрузить ещёbutton>; > handleClick()  this.props.loadMore(); > >

Дебаунсинг гарантирует, что функция не будет выполняться до тех пор, пока не пройдёт определённое количество времени с момента её последнего вызова. Этот метод пригодится, если вам нужно провести ресурсоёмкий расчёт в ответ на событие, которое может быстро повториться (например, прокрутка страницы или нажатие клавиш). В примере ниже для ввода текста используется задержка в 250 мс.

import debounce from 'lodash.debounce'; class Searchbox extends React.Component  constructor(props)  super(props); this.handleChange = this.handleChange.bind(this); this.emitChangeDebounced = debounce(this.emitChange, 250); > componentWillUnmount()  this.emitChangeDebounced.cancel(); > render()  return ( input type="text" onChange=this.handleChange> placeholder="Поиск. " defaultValue=this.props.value> /> ); > handleChange(e)  this.emitChangeDebounced(e.target.value); > emitChange(value)  this.props.onChange(value); > >

requestAnimationFrame — это способ организации очереди функции, которая будет выполнена в браузере за оптимальное время для производительности отрисовки. Функция, поставленная в очередь с помощью requestAnimationFrame , запустится в следующем кадре. Браузер приложит все усилия, чтобы обеспечить 60 кадров в секунду (60 fps — frames per second). Однако, если браузер не в состоянии справиться с этой задачей, он естественным образом ограничит количество кадров в секунду. Например, если ваше устройство поддерживает только 30 fps, то и получите вы только 30 кадров. Использование requestAnimationFrame для троттлинга является очень полезным методом, так как помогает предотвратить выполнение более 60 обновлений в секунду. Если вы выполняете 100 обновлений в секунду, это создаёт лишнюю работу для браузера, которую пользователь всё равно не заметит.

Примечание:

Использование этой техники захватит только последнее опубликованное значение в кадре. Пример работы данной оптимизации вы можете увидеть на MDN

import rafSchedule from 'raf-schd'; class ScrollListener extends React.Component  constructor(props)  super(props); this.handleScroll = this.handleScroll.bind(this); // Создаём новую функцию для планирования обновлений. this.scheduleUpdate = rafSchedule( point => this.props.onScroll(point) ); > handleScroll(e)  // Планируем обновление при активизации события прокрутки. // Если в рамках кадра мы получаем много обновлений, публикуем только последнее значение. this.scheduleUpdate( x: e.clientX, y: e.clientY >); > componentWillUnmount()  // Отменяем любые ожидающие обновления, так как компонент будет демонтирован. this.scheduleUpdate.cancel(); > render()  return ( div style= overflow: 'scroll' >> onScroll=this.handleScroll> > img src="/my-huge-image.jpg" /> div> ); > >

Тестирование ограничения скорости

Когда вы тестируете, что ваш код ограничения скорости работает правильно, полезно иметь возможность прокрутить время. Если вы используете jest , вам может пригодиться mock timers . Если вы используете requestAnimationFrame , то raf-stub может оказаться полезным инструментом для управления смены кадров анимации.

Как передать функцию из child в parent компонент?

У меня есть компонент PassportForm, в котором я рендерю компонент Form. В компоненте Form у меня есть функция Register они из react hook form, но вряд ли это важно, проще говоря у меня есть компонент родителя и дочерний компонент, в дочернем есть функция, а вызывать её нужно в родительском компоненте, я что-то подзабыл как работают коллбэки, подскажите как написать, чтобы функция register работала в родительском компоненте?

const PassportForm = (): JSX.Element => < return ( toolName= fields= files= createConfig= onFilesSubmit= sharepointPaths= /> ); 
const Form = (< depart, toolName, fields, files, createConfig, onFilesSubmit, handleSpChange, >: Props): JSX.Element => < const < register, handleSubmit, formState: < errors, isValid >, trigger, > = useForm(< mode: 'all', >); > 

Отслеживать

задан 11 июл 2022 в 14:59

Алексей Мелентьев Алексей Мелентьев

199 12 12 бронзовых знаков

как передать компонент в компонент react

Пример передачи компонента в другой компонент через свойство props :

function ParentComponent(props)  return ( div> h1>Parent Componenth1> props.childComponent> div> ); > function ChildComponent()  return p>Child Componentp>; > function App()  const myComponent = ChildComponent />; return ParentComponent childComponent=myComponent> />; > 

Здесь мы создаем компонент ParentComponent , который принимает props и рендерит свойство childComponent . Затем мы создаем компонент ChildComponent , который мы хотим передать в «ParentComponent».

В функции App мы создаем компонент «myComponent», который содержит ChildComponent , а затем передаем его в ParentComponent через свойство childComponent .

Таким образом, при рендеринге App мы увидим ParentComponent , который содержит ChildComponent .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *