Pull to refresh
2665.92
RUVDS.com
VDS/VPS-хостинг. Скидка 15% по коду HABR15

Ответы на распространённые вопросы о шаблоне render prop

Reading time 5 min
Views 5K
Original author: Kent C. Dodds
Кент С. Доддс, автор материала, перевод которого мы публикуем сегодня, говорит, что недавно группа программистов, с которыми он должен был провести тренинг по React, заинтересовалась шаблоном «render props», который ещё называют шаблоном «функция как потомок» (в разных публикациях его упоминают как «children as a function» или «function as child»). Документация по React определяет «render prop» как простую технику передачи кода между компонентами React с использованием свойства, значением которого является функция. Компонент, использующий render prop, принимает функцию, которая возвращает элемент React. Этот компонент вызывает данную функцию вместо реализации собственной логики рендеринга. Кент рекомендует тем, кто не знаком с шаблоном render prop, почитать этот материал и посмотреть это видео, а потом уже читать дальше.

image

Кент говорит, что, готовясь к занятиям, он экспериментировал и решил спросить своих читателей в Твиттере о том, какие вопросы у них есть по поводу render props, о том, подробности о каких ещё шаблонах, связанных с этим, им хотелось бы узнать, и о том, какие примеры помогли бы им с render props разобраться. В ответ на свой твит Кент получил немало откликов. В результате он решил остановиться на трёх наиболее типичных вопросах, касающихся шаблона render prop и ответить на них, проиллюстрировав ответы примерами.

Вопрос №1: Как использование шаблона render prop влияет на производительность?


Пожалуй, этот тот самый вопрос, который чаще всего возникает, когда я рассказываю о шаблоне render prop. Кроме того, в комментариях к вышеупомянутому твиту несколько раз спрашивали именно о производительности. Мой ответ на этот вопрос очень прост. Тут я ссылаюсь на материал Райана Флоренса, касающийся использования функций и производительности в React. Если процитировать основные выводы этого материала, то можно сказать следующее:

  • Пишите код так, как привыкли, реализуя в нём ваши идеи.
  • Проводите замеры производительности для того, чтобы находить узкие места. Здесь можно узнать о том, как это сделать.
  • Используйте PureComponent и shouldComponentUpdate только при необходимости, пропуская функции, являющиеся свойствами компонента (только если они не используются в перехватчиках событий жизненного цикла для достижения побочных эффектов).

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

Мне хотелось бы дополнить эти выводы. Если вас действительно беспокоит встраивание render prop-функций и возможное негативное влияние этого на производительность — просто не используйте встроенные функции.

class MyComp extends React.Component {
  renderDownshift = downshift => (
    <div>{/* Элементы пользовательского интерфейса */}</div>
  )
  render() {
    return (
      <Downshift>
        {this.renderDownshift}
      </Downshift>
    )
  }
}

Вопрос №2: Как, при использовании render props, избежать появления чрезмерно усложнённого кода рендеринга, который тяжело читать?


Один из моих читателей, Марк Эриксон, задался вопросом о том, как, используя шаблон render props, не помещать всю логику рендеринга во вложенные функции внутри метода render(). Он хотел знать, можно ли, в угоду повышения читаемости кода, разбить эту логику на небольшие функции, но при этом не отказываться от render props.

Марк задаёт правильные вопросы. Почти все примеры использования шаблона render prop, которые мне доводилось видеть, сводятся к размещению всей логики рендеринга в одной функции. Обычно в подобных случаях можно видеть команду возврата и одно гигантское выражение. Меня эти огромные функции рендеринга раздражали, но я стал относиться к ним гораздо лучше, когда понял, что единственная причина, по которой они мне не нравились, заключается в том, что я думал, что они были сложными, хотя на самом деле это не так.

В любом случае, так как то, что мы называем «render props» — это всего лишь функции, которые вызываются с аргументами, с ними можно делать всё, что душе угодно. В результате я подготовил для Марка пару примеров.


Примеры для Марка

Вот сокращённый вариант кода этих примеров.

function JustARegularFunctionComponent(props) {
  // тут можете делать что хотите
  return <div>{/* ваш код */}</div>
}
function App() {
  return (
    <div>
      <div>
        With a totally different component.
        Thanks React composibility!
      </div>
      <RenderPropComp
        render={arg => (
          <JustARegularFunctionComponent {...arg} />
        )}
      />
      <hr />
      <div>
        Inline! You don't have to make it an
        implicit return arrow function 
      </div>
      <RenderPropComp
        render={arg => { // <-- notice the curly brace!
          // тут можете делать что хотите
          return <div>{/* ваш код */}</div>
        }}
      />
    </div>
  )
}

Вопрос №3: Как организовать доступ к аргументам render prop-функций в методах обработки событий жизненного цикла компонента?


Ещё один достаточно распространённый вопрос заключается в том, как получить доступ к аргументам render prop-функции в методах обработки жизненного цикла компонента. Сложность тут заключается в том, что эти функции вызываются в контексте render компонента, поэтому неясно, как работать с данными, передаваемыми им, скажем, в componentDidMount.

На самом деле, ответ на этот вопрос скрыт в ответе на вопрос Марка, который мы разбирали выше. Обратите внимание на то, что благодаря возможностям React по композиции мы можем создать отдельный компонент и просто перенаправить аргументы в свойства нашего компонента. Например, это можно сделать так.

class RegularClassComponent extends React.Component {
  componentDidUpdate() {
    // вот то, что вам нужно
    console.log(this.props.whatever)
  }
  render() {
    return <div>{/* код пользовательского интерфейса */}</div>
  }
}
function App() {
  return (
    <RenderPropComp
      render={arg => <RegularClassComponent {...arg} />}
    />
  )
}

Мой друг Донавон опечалится, если я не расскажу о его любимом шаблоне «внедрение компонента» (component injection). С использованием этого шаблона добиться нашей цели можно ещё проще, да и код будет выглядеть чище:

class RegularClassComponent extends React.Component {
  componentDidUpdate() {
    // вот то, что вам нужно
    console.log(this.props.whatever)
  }
  render() {
    return <div>{/* код пользовательского интерфейса */}</div>
  }
}
function App() {
  return (
    <CompInjectionComp component={RegularClassComponent} />
  )
}

Над деталями реализации вы можете поработать самостоятельно. Кроме того, можете взглянуть на новую библиотеку, которую создал Донавон после нашего разговора о render props.

Итоги


Я считаю, что шаблон render prop — это просто замечательная технология, и я ожидаю пополнения списка awesome-react-render-props, над которым работает Джаред Палмер. В этом шаблоне мне особенно нравится то, что он может включать в себя логику компонента и при этом не вредить возможностям настройки системы и не усложнять разметку. Думаю, в том, что касается render props, нас ждёт ещё много интересного.

Уважаемые читатели! Пользуетесь ли вы шаблоном render props в своих React-проектах?
Tags:
Hubs:
+12
Comments 9
Comments Comments 9

Articles

Information

Website
ruvds.com
Registered
Founded
Employees
11–30 employees
Location
Россия
Representative
ruvds