Изменить функцию рендеринга внешнего компонента React (без доступа)

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

class Button extends React.Component {

 // ... more code above  
 render() {
  const { onClick, disabled, children} = this.props;

  return (
    <button className={this.getClasses()} onClick={onClick} disabled={disabled}>
      {this.props.symbol && <Icon symbol={this.props.symbol} />}
      {children}
    </button>
  );

 }

}

Как я могу добавить некоторые функции без доступа к файлу (я могу создать свой собственный компонент, расширяющий кнопку)? Например, я хочу, чтобы там была стойка type. Я думал, что могу просто создать <ButtonExtend onClick={resetState} type="button />.

Как я могу это сделать? В идеале я хотел бы сделать это еще более гибким, чтобы я также мог: <ButtonExtend onClick={resetState} type="submit" name="extended button" />.

Я ожидал, что html отобразит все свойства из <Button> с моими дополнительными атрибутами html. Поэтому я хочу использовать функциональность оригинала и моих дополнительных реквизитов. Или это даже невозможно, изменить метод рендеринга другого компонента, если компонент не делает это возможным?


person phifa    schedule 31.10.2018    source источник
comment
Просто расширьте свой собственный класс / компонент из Button и переопределите его метод render   -  person hindmost    schedule 31.10.2018
comment
вы не можете вставлять атрибуты в элемент html кнопки, даже украшая компонент. Вы можете создать подкласс и переопределить метод рендеринга, если в исходном компоненте все еще остается какой-то повторно используемый код.   -  person abidibo    schedule 31.10.2018
comment
@hindmost Разве это невозможно в композиционном плане?   -  person phifa    schedule 31.10.2018
comment
@abidibo у вас есть пример? Не уверен, смогу ли я следовать   -  person phifa    schedule 31.10.2018
comment
Конечно, это возможно так же, как Button расширяется от React.Component   -  person hindmost    schedule 31.10.2018
comment
@hindmost Если возможно, не могли бы вы привести пример кода?   -  person phifa    schedule 31.10.2018


Ответы (2)


Хотя общедоступные методы и свойства компонента доступны refs (https://reactjs.org/docs/refs-and-the-dom.html) шаблон, который вы ищете, - это компоненты высокого порядка (HOC, https://reactjs.org/docs/higher-order-components.html)

person Mosè Raguzzini    schedule 31.10.2018
comment
Я не смог найти пример HOC, который изменяет метод рендеринга так, как мне это нужно. У вас есть пример? - person phifa; 31.10.2018
comment
Какой реквизит вам нужен и какой рендер вы ожидаете? - person Mosè Raguzzini; 31.10.2018
comment
Например, я хочу, чтобы там была опора типа. Поэтому я могу сказать type = «button» или type = «submit». Я ожидаю, что будет отображена простая кнопка. - person phifa; 31.10.2018

Если компонент не был разработан для настройки, нет простого способа сделать это.

Button - пример плохо спроектированного компонента, потому что он не принимает дополнительных свойств. Проблема и PR могут быть отправлены в репозиторий для решения исходной проблемы.

В расширенном компоненте это можно исправить, передав свойства из расширенного компонента.

Родительский render результат может быть изменен:

class ButtonExtend extends Button {
 // ... more code above  
 render() {
  const button = super.render();
  const { symbol, children, ...props } = this.props;

  return React.cloneElement(button, {
    children: [
      symbol && <Icon symbol={symbol} />,
      ...children
    ],
    ...props
  });
}

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

Более чистый способ - вставить render в расширенный компонент и изменить его:

class ButtonExtend extends Button {
 // ... more code above  
 render() {
  const { symbol, children, ...props } = this.props;

  return (
    <button className={this.getClasses()} {...props}/>
      {symbol && <Icon symbol={symbol} />}
      {children}
    </button>
  )
 }
}

Таким образом, его можно использовать как

<ButtonExtend onClick={resetState} type="submit" name="extended button" />
person Estus Flask    schedule 31.10.2018
comment
если компонент является внешним (из пакета NPM, IE) и конкретная опора не ожидается, клонирование элемента и слияние реквизитов может быть бесполезным. - person Mosè Raguzzini; 31.10.2018
comment
Что вы имеете в виду под словом «не ожидал»? onClick и т. д. ожидаются. Это приведет к повторному рендерингу кнопки с желаемыми реквизитами. Попробуй сам. - person Estus Flask; 31.10.2018
comment
Он пытается получить другой рендер, поэтому ивентов и стандартных реквизитов может не хватить - person Mosè Raguzzini; 31.10.2018
comment
Я проголосовал против, потому что разработчики React продвигают композицию над наследованием, а ваше решение включает наследование, даже если в React есть шаблон для управления такими случаями. В Facebook мы используем React в тысячах компонентов, и мы не нашли ни одного варианта использования, в котором мы бы рекомендовали создавать иерархии наследования компонентов. ref: reactjs.org/docs/composition-vs-inheritance.html - person Mosè Raguzzini; 31.10.2018
comment
@ MosèRaguzzini Я прекрасно понимаю, что композиция важнее наследования. React продвигает композицию там, где это оправдано, а не как догму, которой следует слепо следовать. Результатом переопределения render является крайний случай, который не может быть эффективно решен с помощью одной только композиции. Очевидно, вы не пробовали свой собственный ответ. Попробуйте сделать это с помощью HOC, как вы предлагаете, и посмотрите, что произойдет. В конечном итоге вы каким-либо образом расширите класс и вызовете метод render компонента из HOC любым способом, как показано здесь, чтобы расширить его (см. super.render() в коде вопроса). - person Estus Flask; 31.10.2018
comment
@estus привет, спасибо за мудрость, ты сказал, что я должен отправить пиар. Я сделаю это независимо от того, какой подход я выберу сейчас, спасибо! Как этот человек должен спроектировать свой компонент, чтобы я мог добавить еще несколько реквизитов? - person phifa; 31.10.2018
comment
@phifa <button> должна принимать остальные реквизиты как {...this.props}, как во втором фрагменте. Обратите внимание, что вам не нужно явно передавать свойства с одинаковыми именами, например onClick, children, они будут переданы таким образом. Для других компонентов это может быть иначе, но для Button это так. - person Estus Flask; 31.10.2018
comment
Я считаю, что это действительно плохая реализация. Это из сборника рассказов, это компоненты, которые я должен использовать. Я добавил {...this.props} к <Button>, и теперь все отображается на самой кнопке html. Так что не только type = button, что хорошо, но и внутренние информационные свойства, такие как _3 _... Я обновил код в исходном сообщении, так что вы видите, где отображается символ. Это значок в кнопке. Это не должно отображаться в атрибуте html кнопки ‹button› - person phifa; 01.11.2018
comment
@phifa Если вам не нужно передавать все реквизиты, вам нужно отфильтровать те, которые не нужно передавать. Это делается с помощью деструктуризации. Я обновил ответ, чтобы показать, как обрабатывается children в этом случае. Надеюсь это поможет. - person Estus Flask; 01.11.2018