React: Добавьте API фрагментов, чтобы разрешить возврат нескольких компонентов из рендера.

Созданный на 2 сент. 2014  ·  148Комментарии  ·  Источник: facebook/react


Примечание от сопровождающих:

Мы знаем, что это проблема, и мы точно знаем, какой набор проблем можно решить. Мы тоже этого хотим, но это _серьезная проблема_ с нашей текущей архитектурой. Дополнительные комментарии, выражающие желание использовать эту функцию, бесполезны. Не стесняйтесь подписаться на выпуск (в правом столбце есть кнопка), но не комментируйте, если вы не добавляете ценности обсуждению. «Я тоже» и «+1» не являются ценными, как и варианты использования, которые уже были написаны в комментариях (например, мы знаем, что вы не можете поместить элементы <tr> или <dd> с <div> ).


Рассмотрим следующее:

var ManagePost = React.createClass({

  render: function() {
    var posts = this.props.posts

    var something;
    var somethingelse;

    var row = posts.map(function(post){
      return(
        <div>
          <div className="col-md-8">
          </div>
          <div className="cold-md-4">
          </div>
        </div>
      )
    });

    return (
        {row}
    );
  }

});

Если вы удалите <div></div> в map , вы получите следующую ошибку: _Смежные элементы XJS должны быть заключены в закрывающий тег_

только после того, как я повторно добавлю окружающие и довольно бессмысленные div, он компилируется без проблем. У меня 0.11.1

Это решается? Он добавляет лишний, и опять же - ИМО - бесполезный и бессмысленный html на страницу, который ничему не вредит - выглядит грязно и непрофессионально. Может я просто что-то не так делаю, если что, просветите.

Самый полезный комментарий

Я думаю, мы можем закрыть это.

Возврат массивов из компонентов поддерживается начиная с React 16 Beta 1, которую вы можете попробовать прямо сейчас .

Есть еще некоторые ограничения (поддержка SSR не готова), но мы отслеживаем их в #8854 и исправим до финального 16-го релиза.

Спасибо всем за ваши отзывы!

Все 148 Комментарий

Потому что, когда вы не кладете обертку, она обесценивается до этого:

return React.DOM.div(...)React.DOM.div(...)

Что не имеет синтаксического смысла. Страница компилятора jsx может помочь, если вам нужно визуальное сопоставление.

При этом вместо этого можно уменьшить сахар до [div, div] . Это сложно, несколько спорно и не будет реализовано в ближайшем будущем.

(Я не думаю, что это особенно спорно, но это усложняет код и еще не было сделано.)

IIRC @syranide имеет несколько комментариев по этому поводу

@chenglou Хе-хе.

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

Простота возврата не более одного компонента означает, что очень легко рассуждать о том, что вы видите, если нет, <table><TableHeader /></table> может фактически отображать любое количество строк, у вас нет другого способа узнать, кроме как проверить TableHeader и любые составные компоненты, которые он возвращает.

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

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

@sebmarkbage Вероятно, у него тоже есть несколько комментариев :)

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

<>
  <div className="col-md-8">
  </div>
  <div className="cold-md-4">
  </div>
</>

ИЛИ

[
  <div className="col-md-8">
  </div>,
  <div className="cold-md-4">
  </div>
]

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

Но на самом деле единственная причина, по которой мы не поддерживаем это прямо сейчас, заключается в том, что это сложно реализовать. Надеюсь, мы сможем поддерживать его когда-нибудь в будущем, но, вероятно, не в ближайшее время. Извиняюсь. :/

может ли это не повлиять на такие вещи, как jquery или другие библиотеки, предназначенные для определенных элементов, поэтому, если вы сделаете что-то вроде $('#id-name').children() , следующее:

<div id="id-name">
  <div>
    <div class="class-name">
    </div>
  </div>
</div>

в этом случае будут выбраны <div> и <div class="class-name"> . (если я правильно это понимаю)

Это также влияет на селекторы CSS точно так же, как @AdamKyle ранее писал.

Есть обновления по этому вопросу?

Потратил пару минут на то, чтобы понять, почему мой компонент не работает. Я чувствую, что где-то должна быть заметка, может я пропустил? Может быть, явно неправильно пытаться:

var Optimistic = React.createClass({
  render: function() {
    return ( 
      <h1>{this.props.name} loves React</h1>
      <p>React doesn’t. Idea: sprinkle some divs here and there.</p>
    );
  }
});

React.render(
  <Optimistic name="Peter" />,
  document.getElementById('myContainer')
);

@gabssnake Вы должны были получить ошибку компиляции JSX с ошибкой «Смежные элементы XJS должны быть заключены в закрывающий тег»; вы не увидели ошибку или не понятно в ее объяснении?

Спасибо за ваш ответ @spicyj. Ну, я имел в виду уведомление в документации React. Да, консоль действительно показывала ошибку, но необходимость переноса сначала не имела для меня смысла. Вот почему я искал и попал сюда.

У меня тоже была эта боль... особенно болезненная для моего дизайнера, на самом деле. Было бы неплохо, если бы компонент мог выводить узел (следовательно, список узлов или фрагмент) вместо элемента.

Просто говорю.. Я не выступаю за возврат нескольких дочерних элементов из компонента _но_ я хотел бы сделать это в методах render* , которые я извлекаю из render :

  render: function () {
    return (
      <div className={this.getClassName()}
           style={{
             color: this.props.color,
             backgroundColor: this.props.backgroundColor
           }}>
        {condition ?
          this.renderSomething() :
          this.renderOtherThing()
        }
      </div>
    );
  },

  renderSomething() {
    return (
      <>
        <div className='AboutSection-header'>
          <h1>{this.props.title}</h1>
          {this.props.subtitle &&
            <h4>{this.props.subtitle}</h4>
          }
        </div>,

        {hasChildren &&
          <div className='AboutSection-extra'>
            {this.props.children}
          </div>
        }
      </>
    );
  }

Но я, вероятно, должен просто заткнуться и использовать key s.

@gaearon Вы можете сделать это уже сейчас, вам просто нужно вернуть массив (что немного громоздко, хотя да) ... buuuuut, вы можете обойти это, я взломал свой собственный компонент <Frag> который переводится в массив (перегруженный React.render ) ... вы также можете сделать return <NoopComp>...</NoopComp>.props.children я полагаю, если вы хотите избежать взлома.

РЕДАКТИРОВАТЬ: Плохо, я перегрузил React.createElement , а не React.render .

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

@gaearon Да, вы можете пока избегать запятых, используя любой из двух моих обходных путей (если вы сочтете любой из них приемлемым) ... но что вы имеете в виду с явными ключами?

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

@gaearon Ах да, я решил просто мысленно проигнорировать это предупреждение на данный момент :), если вы действительно хотите его избежать, вы можете сделать <MyComp children={this.renderWhatever()} /> , чтобы избежать его ( РЕДАКТИРОВАТЬ: хотя вы, очевидно, не можете использовать это если у вас есть соседние дочерние элементы, вы можете использовать помощника по сглаживанию... но да).

Еще один случай, с которым я столкнулся с набором пользовательского интерфейса. Когда вы помещаете детей в фиксированный прокручиваемый контейнер, например:

return (
  <div style={{
    position: fixed
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow-y: scroll;
  }}>
    {this.props.children}
  </div>
);

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

У меня есть еще один вариант использования, который я подробно описал здесь #3415. Но пока я могу обойти это и понимаю, что это сложно реализовать и редко.

Я ничего не знаю о внутренних элементах реакции (пока), но я просто подкину идею, как вы можете пометить такие виртуальные родительские элементы/фрагменты в DOM: комментарии. Например

<A>
    <B></B>
    <Fragment>
        <C></C>
        <D></D>
    </Fragment>
    <E></E>
</A>

будет оказывать

<a>
    <b></b>
    <!--<fragment data-reactid="">-->
        <c></c>
        <d></d>
    <!--</fragment>-->
    <e></e>
</a>

Это означает, что c и d будут рассматриваться как дочерние элементы sth. (включая вложенный reactid, чтобы не конфликтовать с b и e). Я видел, как другие фреймворки «неправильно используют» комментарии для подобных семантических заданий.

@Prinzhorn Я думаю, вы можете спутать вывод DOM с JSX React.

В случае, если это полезно: предложение @Prinzhorn использовать комментарии HTML для сопоставления «фрагментных компонентов» с DOM — это тот же подход, который использует Knockout. Knockout называет это «виртуальными элементами».

<!-- ko component: "message-editor" -->
<!-- /ko -->

(из документов нокаута )

Кроме того, еще один вариант использования для этого — дополнительные элементы обертки могут быть проблематичными при использовании flexbox.

@aldendaniels абсолютно согласен, я уже сталкивался с проблемами flexbox.

Я столкнулся с этим с flexbox с зависимыми выпадающими списками. Где A будет отображать и управлять первым раскрывающимся списком, за которым следуют B, C или D, в зависимости от значения раскрывающегося списка A, каждый из которых будет отображать соответствующее количество раскрывающихся списков, например

<A>
 <Drop />
 <C><Drop /><Drop /></C>
</A>

A, B, C и D не имеют состояния, поэтому я изменил их с class B {render(){ this.props }} на function B(props){ return [...]; } .

В этом случае для родителя не имеет большого значения, что отображается несколько дочерних элементов, это просто для работы с моим CSS. Есть вещи, которые я бы сделал по-другому, если бы их не нужно было использовать повторно (другому компоненту нужны B, C и D).


Как вариант, может быть способ распутать его от родителя? У меня нет конкретных представлений о том, как это будет выглядеть.

Сегодня я столкнулся со сценарием, который, как мне кажется, является хорошим вариантом использования этой функции: компонент, который отображает несколько элементов <script> , где он может отображаться в элементе <head> страницы. Любой элемент-обертка будет плохим.

В моем сценарии требуется компонент, отвечающий за рендеринг тега <script> как для кода времени выполнения, необходимого на странице, так и для другого тега <script> , который содержит локализованные строки, которые будут использоваться код времени выполнения. Например:

<html>
    <head>
        <script language="runtime.resources.en-us.js"></script>
        <script language="runtime.js"></script>
    </head>
    <body>
    ...
    </body>
</html>

В этом случае я бы хотел, чтобы код был написан как:

var RuntimeScripts = require('./Runtime')
...
return (
    <html>
        <head>
            <RuntimeScripts language="en-us" />
        </head>
    </html>
)
...

Я также столкнулся с некоторыми проблемами с flexbox. Нет ничего, что нельзя было бы решить с помощью CSS, но одна из «прелестей» flexbox заключается в том, что вам нужно меньше элементов «обертки» везде, чтобы ваш макет работал, но вы все равно получите элементы-оболочки повсюду при использовании React, поскольку вы всегда оборачиваете все, что возвращаете, в div/div или подобное, если только не имеет смысла иметь контейнер.

Я почти уверен, что для всех представленных здесь вариантов использования вы могли бы заменить <BunchOfComponents /> на {getBunchOfComponents()} , и визуальный вывод был бы таким же, без введения практических и технических проблем, связанных с наличием компонентов с фрагменты как корень.

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

Кроме того, если вы используете простой coffeescript, легко вернуть массив, поэтому, пожалуйста, отделите функциональность от представления jsx.
IOW, если легко обрабатывать возвращенный массив элементов, не ждите, пока jsx догонит.

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

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

Кроме того, если вы используете простой coffeescript, легко вернуть массив, поэтому, пожалуйста, отделите функциональность от представления jsx.

Это не имеет значения, и это не проблема с JSX. Большая проблема заключается в том, что вы теряете техническое, практическое и интуитивное предположение, что один компонент = один элемент/узел . Я не могу говорить за разработчиков, но я бы не отказался от этого добровольно, это очень полезное предположение. Я уверен, что есть такие же хорошие или лучшие оптимизации, которые можно было бы разработать, если оптимизация — единственная причина, по которой люди этого хотят.

@syranide самая большая проблема в том, что вы не всегда можете использовать обертку
элемент в html, как в таблицах, списках, flexbox, head... Работаем
что приводит к уродливому коду.

Я был бы вполне доволен виртуальным элементом-оболочкой, который отображает только
комментарии, как предлагалось ранее.

Пт, 29 мая 2015 г., 15:56 Андреас Свенссон, [email protected]
написал:

@syranide https://github.com/syranide , но каждый раз, когда один из
компоненты изменяются, все его братья и сестры нуждаются в пересчете...

@wmertens https://github.com/wmertens Да, но много раз вы бы
имейте это в любом случае, потому что родитель должен будет повторно отображать для других
причинам, или просто потому, что вы все равно получаете данные через реквизит. Но
да, в этом разница, но это не значит, что такой подход правильный,
это оптимизация, и есть много способов ее выполнить.

Также, если вы используете простой coffeescript, легко вернуть массив, поэтому, пожалуйста,
отделить функциональность от представления jsx.

Это не имеет значения, и это не проблема с JSX. Большая проблема в том, что
вы теряете техническое, практическое и интуитивное предположение _one
компонент = один элемент/узел_. Не могу говорить за разработчиков, но я бы не стал
откажитесь от этого добровольно, это очень полезное предположение. Я уверен
есть такие же хорошие или лучшие оптимизации, которые могли бы быть дизайном, если бы
оптимизация — единственная причина, по которой люди этого хотят.


Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/facebook/react/issues/2127#issuecomment-106810565 .

fwiw, относительно легко взломать компонент «фрагмент» в React, который React обрабатывает как массив своих дочерних элементов. Он будет автоматически генерировать ключи, но, поскольку это происходит после первоначальной проверки компонентов, он не выдаст обычную ошибку «ключи не предоставлены».

С учетом сказанного, этот хак решает только то, о чем @gaearon говорил выше - не нужно иметь дело с уродливым синтаксисом массива/настройкой произвольных ключей в вашем JSX, а не с проблемой возврата нескольких узлов в корне метода рендеринга компонента.

У меня проблема с идеей, что компонент должен возвращать один "элемент/узел". Мне кажется совершенно разумным для структуры JSX:

<Main>
  <Foo />
  <Fragment>
    <Bar />
    <Baz />
  </Fragment>
</Main>

чтобы закончить как DOM:

<div>
  <div>Foo</div>
  <div>Bar</div>
  <div>Baz</div>
</div>

Я не думаю, что это нарушение принципа наименьшего удивления, потому что компоненты уже делают всевозможные «удивительные вещи» с DOM, используя хуки жизненного цикла (просто посмотрите на общий шаблон червоточины). Общепризнано, что компонент не обязательно приведет к созданию одного элемента, и это нормально, потому что для работы с DOM приходится идти на некоторые компромиссы.

Дело не в «оптимизации» и даже не в том, что вам не нравится синтаксис массива. Как уже упоминали многие пользователи, _элементы-оболочки серьезно нарушают стиль и макет_. Таблицы наиболее очевидны, но Flexbox также является серьезной проблемой. У меня уже есть CSS, который просто повторно применяет гибкие правила к элементам-оболочкам, которые существуют только благодаря React, и это довольно уродливо.

Для всех представленных здесь вариантов использования я уверен, что вы могли бы заменить с {getBunchOfComponents()}, и визуальный вывод будет таким же, без введения практических и технических проблем, связанных с наличием компонентов с фрагментами в качестве корня.

Это требует от разработчиков компромисса при создании изолированных повторно используемых компонентов — да поможет им Бог, если они решат, что хотят повторно использовать свою группу компонентов в другом месте — из-за основной проблемы реализации в React. Я не думаю, что это следует принимать.

@thomasboyt

РЕДАКТИРОВАТЬ: Моя ошибка, я объединил некоторые из ваших аргументов с обсуждением таблицы выше, я в основном согласен с тем, что вы говорите, я думаю. Но по-прежнему существуют проблемы с непрозрачностью компонентов, поэтому то, что задумано как полезная прозрачная оболочка, становится непрозрачным для родителя. Представьте, что <Wrapper1><Wrapper2>...</Wrapper2></Wrapper1> , Wrapper1 не может видеть потомков Wrapper2 . Так что, возможно, wrapMyElements(...) просто является лучшим решением во всех отношениях (включая любые другие необходимые вспомогательные функции).

У меня проблема с идеей, что компонент должен возвращать один "элемент/узел". Мне кажется совершенно разумным для структуры JSX:

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

Но я думаю, что очень важным вопросом является удобочитаемость, которая, ИМХО, является самым большим преимуществом React, все ясно.

<table>
  <tr>
    <td />
    <td />
    <td />
  </tr>
  <tr>
    <Columns1 />
    <Columns2 />
  </tr>
</table>

Глядя на это нет смысла, откуда берется 3-я ячейка? Возможно, это на самом деле неправильно, и это рендеринг 2 или 4 ячеек, кто знает, возможно, это на самом деле динамическое и зависит от пропса или внешнего состояния? Существует множество вариантов этой проблемы, которые становятся более неприятными, когда вы рассматриваете другие внешние интерфейсы, отличные от HTMLDOM, которые могут иметь явные ожидания. Еще одна вещь, которую следует учитывать, это то, что элементы непрозрачны, поэтому, если вы замените <tr /> на <MyMagicalTr /> , тогда он не сможет взаимодействовать с отдельными ячейками или даже определить, сколько их, так что даже если <MyMagicalTr /> может принимать только <MyMagicalTd /> , нет никакой гарантии, что он действительно сможет с ними взаимодействовать.

Это требует от разработчиков компромисса при создании изолированных повторно используемых компонентов — да поможет им Бог, если они решат, что хотят повторно использовать свою группу компонентов в другом месте — из-за основной проблемы реализации в React. Я не думаю, что это следует принимать.

«Это требует, чтобы разработчики пошли на компромисс при создании изолированных ...», но это именно проблема, если вы спросите меня, если компонент может возвращать несколько элементов, он больше не изолирован, он заменен, компонент просачивается в родителя.

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

ИМХО, я не вижу проблемы с {getBunchOfComponents()} , это явно, это позволяет нам сохранить наши полезные ожидания. Если производительность является проблемой, тогда React.createSmartFragment() (или w/e) на помощь, прозрачный массив/объектоподобный тип, но такой, который может обновляться независимо от своего родителя.

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

РЕДАКТИРОВАТЬ: Чтобы уточнить, возможно, компоненты смогут возвращать несколько элементов в будущем, потому что есть другие явно полезные варианты использования, особенно в контексте прохождения через дочерние элементы (например, тот, который вы показываете @thomasboyt), сохраняется читабельность.

Я думаю, мне нужно еще немного кофе, прежде чем я смогу ответить на философскую сторону этого разговора (спасибо за очень хорошие замечания, @syranide), но на стороне реализации я начал ковыряться в этом вчера вечером, чтобы увидеть, как возможно изменение этой области, что приводит к этому всплеску: https://github.com/facebook/react/compare/master...thomasboyt:fragment

И подкинул сюда небольшую демонстрацию: http://www.thomasboyt.com/react-fragment-demo/

Некоторые замечания по поводу реализации:

  • Неудивительно, что очень сложно модифицировать систему, которая ожидает, что «1 компонент = 1 узел» будет поддерживать больше узлов;)
  • Первоначально я думал попробовать отслеживать фрагменты на стороне операций DOM, чтобы сгенерированные ReactMultiChild инструкции по мутации могли оставаться прежними и обрабатывать фрагменты, как любой другой узел. Однако я не мог придумать хороший способ добавить состояние о количестве узлов/какие узлы являются фрагментами в отслеживание состояния DOM. Что-то вроде ограждения комментариев, отмеченного @Prinzhorn , может сработать, но я опасаюсь всего, что потребует поиска в DOM, учитывая относительную стоимость.
  • Отбросив эту идею, я добавил поле _nodeCount ко всем дочерним элементам компонента ReactMultiChild , чтобы он мог отслеживать количество корневых узлов, фактически содержащихся во фрагменте.

Проблема в том, что хотя это достаточно легко сделать при начальном рендеринге, просто подсчитав дочерние элементы фрагмента, обновление количества узлов фрагмента при последующих мутациях кажется более сложным. Это до сих пор не сделано в моей ветке (см. https://github.com/thomasboyt/react/issues/2).

  • Многие операции DOM основаны на доступе к родительскому узлу элемента, который просматривается по внутреннему идентификатору узла, для добавления/перемещения/удаления элементов (см. https://github.com/thomasboyt/react/issues/3). Поскольку цикл ReactMultiChild updateComponent отвечает за передачу этого идентификатора, его можно изменить, чтобы выполнить поиск ближайшего родителя, у которого есть узел DOM, но это звучит дорого. В качестве альтернативы может быть возможным иметь внутренний реестр ключей фрагментов для их ключей «настоящего узла».

Я до сих пор не уверен, что требование фрагментов поддерживать количество их корневых узлов — лучший способ сделать это (хотя это, по крайней мере, привело меня к этой демонстрации), и все это было взломано довольно быстро и довольно поздно ночью. , так что если у кого-то еще есть предложения по реализации, не стесняйтесь вмешиваться :>

@thomasboyt IIRC основное препятствие реализации связано с тем, что React ссылается на дочерние узлы с помощью mountIndex , это не работает, когда один «узел» может внезапно стать любым количеством узлов, и это может произойти без вызова родителя, а также может произойти несколько компонентов в глубину (обертывание). Если я не ошибаюсь, довольно тривиально, чтобы React поддерживал несколько корневых элементов, если их число никогда не меняется.

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

@syranide Правильно; решение, над которым я работаю, фактически вводит новый nodeIndex , который должен быть «реальным смещением» узла (что напоминает мне, что мне нужно вернуться и удалить mountIndex , поскольку Я думаю, что это сейчас не используется в моей ветке).

Но, как вы заметили, это проблематично, если количество корневых элементов изменяется, поскольку nodeIndex компонента необходимо обновлять всякий раз, когда изменяется количество узлов предыдущего родственного компонента. Все еще нужно найти решение для этого.

Я также столкнулся с проблемами flexbox. @syranide , не могли бы вы подробнее рассказать о предложенном вами решении «getBunchOfComponents»? Будучи новичком в React, трудно полностью понять, где определить эту функцию / как ее применить.

@landabaso

function getBunchOfComponents(...) {
  return [<ColumnA key="a" />, <ColumnB key="b" />];
}

Привет,

Я не читал всю ветку, но вот вариант использования оптимизации рендеринга, для которого может потребоваться эта функция:

http://stackoverflow.com/questions/30976722/react-performance-rendering-big-list-with-purerendermixin

Если эта функция будет выпущена, ReactCSSTransitionGroup больше не понадобится узел-оболочка, верно?

@slorber Да, наверное, это правда.

Сталкивайтесь с необходимостью этой функции каждый день.

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

Для <div> вы можете обернуть их в <div> , но для строк таблицы <tr> элементов это не так просто. Вы можете обернуть <tr> в <tbody> , но может быть нежелательно, чтобы несколько слоев <tbody> обертывали несколько слоев <tr> .

Сценарий, о котором я рассказал, заключался в попытке получить компонент, предоставляющий элементы <link> и <script> без необходимости полностью становиться средством визуализации <head> .

Я добавил примечание в начало этой проблемы. Пожалуйста, прочтите его, прежде чем комментировать. https://github.com/facebook/react/issues/2127#issue -41668009

Удар... У меня есть большая потребность в этом на стороне сервера. Очень сложно отображать полные веб-страницы (за исключением doctype) без возможности отображения фрагментов из-за раздела <head> . В настоящее время я работаю над этим с помощью миксинов и небольшой логики в финальном рендере, но было бы намного проще, если бы была поддержка рендеринга нескольких компонентов.

@impinball , вы можете попытаться написать что-то похожее на название документа- реакции, основанное на побочном эффекте реакции, чтобы решить эти проблемы. Я смог сделать то же самое для метатегов, заголовков, заголовков и иногда перенаправлений.

Я тоже столкнулся с этой проблемой, есть ли какие-то обходные пути на данный момент? Я не мог заставить {getBunchOfComponents()} работать, как было предложено.

Не что иное, как уже упомянутые.

@jonchay Вы можете создать компонент, который отображает только свои дочерние элементы.

function statelessWrapper(props) {
   return props.children;
}

а затем использовать его:

render() {
   return (  
      <statelessWrapper>
         {renderABunchOfComponents()}
      </statelessWrapper>
    );
}

@whatknight Это не сработает, за исключением случаев, когда return renderABunchOfComponents(); уже работает.

  render () {
    let user = this.state.user
    let profile = user.get('data')
    let view = null

    if (user.get('status') === 'fetched') {
      view = (
        <h1>{profile.get('login')}</h1>
        <img src={profile.get('avatar_url')} />
        <dl>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>
      )
    } else if (user.get('status') === 'fetching') {
      view = <h1>fetching</h1>
    } else if (user.get('status') === 'error') {
      view = <h1>{profile.message}</h1>
    }

    return (
      <div className={className}>
        {view}
      </div>
    )
  }

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

@kilianc в этом случае можно просто написать

      view = [
        <h1 key={0}>{profile.get('login')}</h1>,
        <img key={1} src={profile.get('avatar_url')} />,
        <dl key={2}>
          <dt>email</dt>
          <dd>{profile.get('email')}</dd>
        </dl>,
      ]

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

Мне нужна эта функция по уже указанным причинам, поэтому я попытался реализовать контейнер <frag></frag> на https://github.com/mwiencek/react/tree/frag-component .

Реализация не очень красивая, но если она работает для людей, я могу отправить PR и позволить разработчикам React разобрать ее на части.

@mwiencek Похоже, ваша реализация не работает, если количество дочерних элементов в фрагменте изменяется при обновлении (_nestedChildCount устанавливается только в mountComponent)? Есть небольшая хитрость, чтобы все это работало хорошо. Хотя, похоже, у тебя хорошее начало. Недавно я снова думал об этом, и, возможно, я нашел надежный способ сделать это возможным. Я сообщу, если я найду успех.

@spicyj да, ты прав, мне нужно разобраться с этим...

Тем не менее, мы очень рады, что вскоре мы увидим правильную реализацию. :) Не стесняйтесь копировать тесты из этой ветки, если они вообще вам пригодятся.

@spicyj Разве не лучше использовать createFragment и преобразовать JSX в это? Или мы действительно хотим, чтобы фрагменты были элементами?

Чтобы создать и расширить последний комментарий @syranide , похоже, нет необходимости в дополнительном «Fragment API», если рендеринг позволяет использовать массивы в качестве возвращаемого значения. JSX может преобразовать несколько корневых элементов в массив, который также будет работать для возвращаемых значений любой другой функции. Таким образом, вместо того, чтобы вводить дополнительную поверхность API, которая требует документации и изучения, одно из ограничений React можно было бы просто снять.

Это по крайней мере повлияет на babel-plugin-transform-react-jsx (реализация), а также babel-plugin-syntax-jsx (удаление ошибки синтаксического анализа для соседних корневых элементов). В то время как изменение первого кажется довольно безопасным, я не знаю области/использования последнего и влияния, которое предлагаемое изменение окажет на другие проекты.

Это все еще не распространяется на случай использования условного выражения с несколькими элементами. Я не считаю «Использовать массив и вручную добавлять произвольные key={...} к каждому элементу» как правильное долгосрочное решение.

согласен с @dantman

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

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

Чтобы правильно обрабатывать обновления, я подумал, что решение @spicyj , придуманное для # 5753, может работать и для фрагментов (оборачивая содержимое во что-то вроде <!-- react-frag: 1 --><!-- /react-frag: 1 --> ). Да, комментарии немного уродливы, но это намного надежнее, чем то, что я пытался сделать с помощью _nestedChildCount . Этот подход теперь используется на https://github.com/mwiencek/react/tree/frag-component .

До сих пор я не видел упоминания об этом в ветке, но я думаю, что решение этой проблемы также улучшает компонуемость. Например, представьте, что у вас есть сетка, в которой вы хотите, чтобы ячейки исчезали в определенном порядке. В идеале здесь должно быть задействовано два компонента: один для обработки макета, а другой для обработки анимации. У вас будет такой API:

<GridLayout
  columns = { 3 }
>
  <FadeAnimator
    springConfig = { springConfig }
  >
    { ...cells }
  </FadeAnimator>
</GridLayout>

Это позволит вам переключиться на другой макет или другую анимацию, не зная о деталях реализации другого. GridLayout ожидает получить список дочерних элементов. FadeAnimator перехватит этот список, внедрит соответствующие стили и/или прослушиватели событий и вернет новый список для потребления GridLayout . У FadeAnimator нет причин беспокоиться о размещении сетки, за исключением того, что элементы React не могут возвращать массивы из рендеринга. Более того, нет простого способа заменить сетку, скажем, каменной кладкой, потому что FadeAnimator требуется, чтобы действовать как контейнер для своих дочерних элементов.

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

<FadeAnimator
  wrapper = {
    <GridLayout
      columns = { 3 }
    />
  }
  springConfig = { springConfig }
>
  { ...cells }
</FadeAnimator>

// FadeAnimator
render() {
  return React.cloneElement(
    props.wrapper,
    null,
    props.children
  );
}

но это делает код менее понятным, более сложным и трудным для составления.

Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!
Добавьте API-интерфейс фрагмента, чтобы разрешить возврат нескольких компонентов из рендеринга!

Предложение @texttechne лучше. Вместо того, чтобы вводить дополнительный API, реакция должна обрабатывать несколько корневых элементов при рендеринге.

Я думаю, что обработка нескольких корневых элементов в рендере будет сложной.
Это означает, что там: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L1089

У вас будет массив элементов, а не элемент.
Из-за этого, насколько я понимаю, теперь вам нужно будет создать несколько элементов React: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js# L471

Затем смонтируйте несколько созданных элементов React: https://github.com/facebook/react/blob/master/src/renderers/shared/reconciler/ReactCompositeComponent.js#L471 .

И согласовать всю разметку, произведенную в надлежащем порядке.

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

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

Например, как вы справляетесь, когда один из корней размонтирован? Как вы правильно обрабатываете обновление?
Или как вы управляете несколькими корнями внутри DevTools? (очевидный ответ: исправить DevTools...)

Я думаю, что фрагмент является составным компонентом. Где именно разница?
Если мы в конечном итоге дублируем код для реализации фрагментов, нам лучше реализовать синтаксис сахара, чтобы сохранить внутренности React «нетронутыми»?

Мне интересно, я играл с внутренними компонентами React вокруг вопроса Subtree (renderSubtreeIntoContainer), и я чувствую, что это несколько связано. Когда вы хотите выполнить рендеринг в новое поддерево, вам фактически придется рендерить новый корень. Итак, если мы поддерживаем несколько корней на уровне дерева, мы каждый раз отображаем новые корни:

<p>Hi</p>
<p>There</p>

приведет к двум вызовам «рендеринга в новый корень».

Вместо одного вызова, если бы мы использовали обертку, верно? Как насчет производительности? Простота? Честно говоря, у меня такое ощущение: нам не следует трогать внутренности React, чтобы справиться с этой ситуацией. Скорее, можем ли мы провернуть этот подвиг с помощью JSX? Можем ли мы улучшить синтаксис JSX?

(_Отказ от ответственности_: я не совсем привык к внутренностям React, и могут быть некоторые части, которые я не полностью понимаю или не понимаю. Приношу свои извинения за недопонимание.)

Изменить: исправление/прояснение вещей. Кроме того, GitHub загадочно странно оформляет электронные письма, поэтому мне пришлось переформатировать блок кода... :-(

Привет, основной участник/коммиттер Mithril здесь.

TL;DR: Фрагменты чрезвычайно сложны, даже когда API и внутренние компоненты
просто.

Кстати, по опыту знаю, что это _очень_ сложно реализовать. Этот
также несколько раз запрашивали мифрил, но он был отклонен из-за
явная трудность. Каждая попытка реализовать его потерпела неудачу с
как минимум треть тестового набора провалилась.

Я все еще разрабатываю детали библиотеки vdom, которую планирую написать,
и он будет рассматривать все как фрагмент, но это то, что у вас есть
буквально (пере)писать часть рендеринга с нуля. Как Реакт,
он будет отделен от DOM, но API будет существенно отличаться
концептуально для рендеринга.

Вот загвоздка с фрагментами: ими надо полностью управлять
внутренне, или вы не правильно различаете их. Четное
document.createContextualFragment бесполезно. Просто в качестве примера давайте
преобразовать два дерева, рендеринг опущен:

// Before
A {}
fragment {
  B[class="foo"] {}
  B[class="bar"] {}
}
C {}
D {}

// After
A {}
B[class="foo"] {}
fragment {
  C {}
}
D {}

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

Но когда фрагмент исчез, вам нужно обработать семантику хуков, например
shouldComponentUpdate (я не помню, как React назвал этот хук). Так
вам все равно придется отслеживать фрагменты самостоятельно. Вы различаете их
содержимое, как если бы оно было частью их родительского фрагмента, но у вас все еще есть
чтобы отслеживать положение этого фрагмента ради компонента.

Или, другими словами, компоненты больше не связаны друг с другом неразрывно.
узел ДОМ. Вместо этого они связаны с соответствующим фрагментом. React, как и большинство других библиотек и фреймворков vdom, внутренне связывает компонент с его древовидным представлением, даже с ожидаемыми типами. Это самый простой способ реализовать алгоритм сравнения, который
обрабатывает компоненты. Когда они развязаны, вы должны сделать отдельные
бухгалтерия для обоих. Вы не инициализируете компоненты при инициализации
узел. Теперь это два совершенно отдельных процесса. это трудно сделать
изначально, и еще сложнее добавить поддержку впоследствии.

Спасибо всем за слова. Мы знаем, что это сложно, и все еще держим это в нашем списке дел. (Если спрашивать с энтузиазмом, это не произойдет раньше, @janryWang.)

@isiahmeadows К вашему сведению, ветка перезаписи Mithril поддерживает фрагменты.

@spicyj Вы можете взглянуть на реализацию [1] [2] и тесты [1] [2] , если вы еще не следуете им. Весь двигатель дифференциала составляет всего около 400 LOC, поэтому, надеюсь, за ним будет легко следить.

@isiahmeadows Я думаю, что GitHub съел часть вашего комментария. Блок кода сломан, и я не вижу первого экземпляра <D /> .

Хотя в электронной почте выглядит нормально. Может быть, вы нашли ошибку в GitHub?

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

Я перенаправил исходное электронное письмо на support@github. Надеюсь, они смогут исправить парсер. 😃

@lhorie Вы используете синтаксис массива для фрагмента?
у вас есть полифилл для DocumentFragment?

И использование «псевдо» элемента-оболочки, такого как комментарий HTML, не вариант? Я думал, что именно так текстовые узлы "решались"...

Спасибо за исправление этого комментария @spicyj

Чтобы решить проблемы @isiahmeadows , поднятые в его примере: новый движок Mithril _не_ следует семантике, предложенной @isiahmeadows по нескольким причинам:

  • реализация этой семантики сделала бы сравнение _значительно_ более сложным
  • это затруднило бы рассмотрение ключей и ошибок, связанных с ключами, поскольку ключевое пространство становится возможным просачиваться из компонентов и даже в одноуровневые и дочерние компоненты.
  • это сделало бы жизненные циклы фрагментов неинтуитивными (например, в этом примере B.bar удаляется, но фрагмент также удаляется, и создается новый фрагмент для переноса C). Это нарушает общий принцип «каскадного» жизненного цикла, что означает, что вы больше не можете быть уверены, что дочерний узел удален, если данный родительский узел удален. Как и в предыдущем пункте, это может привести к утечке информации о возможностях инкапсуляции компонента.
  • если, гипотетически, кто-то столкнется с проблемами diff, связанными с механизмом diff, не придерживающимся этой семантики, решение в пространстве приложения будет таким же тривиальным, как обертывание фрагмента вокруг узла-нарушителя.

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

Обратите внимание, что я исправил свой комментарий. Я допустил несколько ошибок, и GitHub плохо справляется со стилем ответов по электронной почте... :frowning:

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

Возможно, React.Children.map должен расширять фрагменты. Если вы хотите выполнить итерацию по каждому дочернему элементу (включая дочерние элементы дочерних фрагментов), используйте Children.map . Если вы хотите рассматривать фрагменты как непрозрачную коробку, работайте напрямую с props.children как с {массивом, элементом}.

@lhorie Я не принимал такого участия в переписывании, поэтому я не так хорошо знаком с его тонкостями. Я был занят тем, что у меня один выпускной на этой неделе и три на следующей, плюс я работаю с кем-то, чтобы организовать стажировку, которую требует мой колледж. Я также был сосредоточен на завершении Techtonic, для которого я _почти_ сделал CLI (тест сломан, чего не должно быть).

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

@appsforartists

Возможно, React.Children.map должен расширять фрагменты

Стабильная версия Mithril делает что-то подобное внутри (т.е. выравнивает подмассивы), но я отхожу от этой модели по соображениям производительности (а также из-за некоторых исторических головных болей, связанных со списками vnode, которые смешивают узлы с ключами и без ключей). Может быть что-то рассмотреть.

И использование «псевдо» элемента-оболочки, такого как комментарий HTML, не вариант? Я думал, что именно так текстовые узлы "решались"...

Мы используем https://github.com/mwiencek/react-packages в продакшене уже несколько месяцев. Он использует этот подход оболочки комментариев, поэтому фрагменты могут быть однозначно вложены.

@mwiencek Можно ли использовать ваш подход без специального пакета реагирования?

@mwiencek нужны ли обертки для комментариев? Я бы не ожидал умных вещей от фрагментов, если вы переместите элемент из фрагмента в родственный фрагмент или корневой элемент, его можно просто воссоздать.

Итак, если вы будете следовать дереву vdom по порядку, вам не нужны комментарии, или они вам нужны?

В любом случае, на первый взгляд, ваше решение выглядит именно тем, что необходимо для решения этой проблемы. 👍

Не строго, но в данном случае они упростили реализацию.

Таким образом, в настоящее время невозможно создать правильные списки описаний <dl> с помощью React?

<dl>
  <dt>Def 1</dt>
  <dd>Some description</dd>
  <dt>Def 2</dt>
  <dd>Some other description</dd>
</dl>

@KaiStapel Эта проблема связана с возвратом нескольких компонентов (или элементов, я думаю) из render() . Пока ваша функция render возвращает только один корневой элемент/компонент, она должна работать.

OK:

render() {
  return (
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

Не хорошо:

render() {
  return (
    <h2>my list</h2>
    <dl>
      <dt>Def 1</dt>
      <dd>Some description</dd>
      <dt>Def 2</dt>
      <dd>Some other description</dd>
    </dl>
  )
}

@GGAlanSmithee Да, жестко закодировано, но вы не можете:

<dl>
   loop here and print out dt/dd pairs
</dl>

Что очень печально. То же самое относится и к таблицам с диапазонами строк, поскольку вы не можете одновременно отображать два элемента <tr> :(

Сверху:

«Я тоже» и «+1» не являются ценными, как и варианты использования, которые уже были написаны в комментариях (например, мы знаем, что вы не можете помещать элементы <tr> или <dd> с <div>) .

Учитывая, что на https://github.com/mwiencek/react-packages есть рабочее решение, есть ли шанс, что оно скоро станет частью React? Или ждем нового реконсилера?

Учитывая, что в https://github.com/mwiencek/react-packages есть рабочее решение.

Вы успешно используете его в реальных проектах?

@mwiencek

Реализация не очень красивая, но если она работает для людей, я могу отправить PR и позволить разработчикам React разобрать ее на части.

Конечно, пожалуйста, пришлите PR!

Что касается подачи PR, последнее, что я слышал от @spicyj , было то, что они хотели закончить новый базовый алгоритм и найти для него правильное решение, поскольку узлы комментариев на самом деле не имеют смысла в React Native. Я не следил за развитием событий, но не думаю, что эти планы изменились. Я рад, что люди находят пакеты полезными в то же время.

Новый алгоритм находится в разработке и поддерживает фрагменты. Однако я бы не ожидал, что он будет готов к производству в ближайшие месяцы. Интересно, не слишком ли плохо добавить это сначала в React DOM, а затем в React Native? Недостатком является то, что он немного фрагментирует экосистему (каламбур!), но это может дать нам некоторое время, чтобы поэкспериментировать с этой функцией. Сегодня у нас собрание команды, поэтому я подниму этот вопрос, если у нас будет время, чтобы лучше понять его.

@gaearon Могу я просто указать, что поддержка фрагментов очень проста, это просто сахар, в настоящее время я сам использую <frag> в виде крошечной тривиальной оболочки. Действительно ли так важно вернуть несколько дочерних элементов в качестве корня компонента?

@syranide Я использую пользовательскую сборку с frag в бета-среде, однако вместо этого я хотел бы использовать официальную сборку React. Можете ли вы предоставить свою обертку <frag> ? :) Спасибо

@амертак

import React from 'react';
import createFragment from 'react-addons-create-fragment';

let nativeCreateElement = React.createElement;

React.createElement = function() {
  if (arguments[0] !== 'frag') {
    return nativeCreateElement.apply(this, arguments);
  }

  let length = arguments.length;
  if (length <= 2) {
    return null;
  }

  let children = {};
  for (let i = 2; i < length; i++) {
    children['~' + (i - 2)] = arguments[i];
  }

  return createFragment(children);
};

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

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

Чтобы лучше понять, над чем мы работаем, ознакомьтесь с нашим репозиторием заметок к собраниям! Вы можете найти наше последнее обсуждение этого вопроса в https://github.com/reactjs/core-notes/blob/master/2016-07/july-07.md.

@gaearon Было бы интересно иметь хотя бы официальный синтаксис фрагментов.

@syranide Спасибо за код, но, к сожалению, кажется, что я не могу его использовать, так как мне нужен frag в качестве корневого компонента приложения, который обрабатывается методом ReactDOM.render , и этот метод не будет принимать фрагмент .

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

@amertak Да, это только для включения более разумного синтаксиса для создания фрагментов, он не добавляет никаких новых функций.

@сиранид
Я подумал, можно ли отобразить комментарий вручную и рассматривать его как еще один компонент (в этом может не быть необходимости)?
Внутренние комментарии обрабатываются как тип #comment , поэтому, возможно, можно вызвать
React.createComponent('#comment', { ... }, children) ?
Просто идея. Небольшой обходной путь.

Главное, чего здесь не хватает, — это возможности рендерить узел comment , я прав? :)

@gaearon Немного грустно, что это не скоро, но я ценю прозрачность. Хорошо пиши!

Альтернативное решение до обновления?
Для меня мне нужно сделать выпадающее меню Botstraap

render(){
    return (
        <ButtonNotification/>
        <ul className="dropdown-menu">
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification")
);

А разве не возможно, идея?
Спасибо,

http://getbootstrap.com/components/#btn-dropdowns-сингл

Он явно заключен в один <div class="btn-group"> , как показано в Документах.

Да, конечно, но есть два элемента, завернутые в <div class="btn-group"> .

render(){
    return (
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listNotification") //is DIV#listNotification
);
<div id="listNotification" class="btn-group"><!--wrap-->
    <a href="#">button notification</a> <!--ELEMENT1-->
    <ul> <!--ELEMENT2-->
        <li></li>
        <li></li>
    </ul>
</div>

И два элемента, завернутые в один элемент, невозможны,
Спасибо за ваше время @Primajin

Просто сдвиньте все на один уровень:

render(){
    return (
        <div className="btn-group"> //WRAP
            <ButtonNotification/> //ELEMENT 1
            <ul className="dropdown-menu"> //ELEMENT 2
                {this.state.items.map(this.addItem)}
            </ul>
        </div>
    );
}
ReactDOM.render(
    React.createElement(NotificationHandler, null),
    document.getElementById("listWrapper") //or whatever your list is called
);

Хорошо, но у меня есть другие элементы в родительском элементе.
Это только переместит проблему.

<div ...> <--!listWrapper-->
    <div class="btn-group">....</>
    <div class="btn-group">....</>
    <--!React-->
    <div class="btn-group"> //WRAP
        <ButtonNotification/> //ELEMENT 1
        <ul className="dropdown-menu"> //ELEMENT 2
            {this.state.items.map(this.addItem)}
        </ul>
    </div>
    <--!React-->
    <div class="btn-group">....</>
</div>

И в этом случае React заменит все содержимое.
Можно ли сделать «добавить» без замены других элементов?

Спасибо,

@ rifton007 это не так работает. Вы можете иметь несколько элементов/компонентов как братьев и сестер, упакованных в контейнер. Ограничение состоит в том, что компонент не может содержать несколько элементов return . Код, который вы только что разместили, будет работать.

При этом, если вы столкнетесь с примером, который не работает, будет вежливо прочитать всю ветку и подумать, добавляете ли вы _новый_ пример или просто повторяете ту же проблему, которая уже была подтверждена. Это известное ограничение, и эта ветка содержит расширенное обсуждение деталей и обоснование этого ограничения. Дэн также заявил, что они намерены в конечном итоге исправить это. Чего вы пытаетесь достичь, указывая на другой пример признанной проблемы?

Извините, я просто хотел знать, есть ли у кого-нибудь альтернативное решение.
Вы можете удалить мои сообщения, если это необходимо.

Я создаю приложение с помощью Framework7 и React, однако формат html framework7 исправлен,

<div class="page"><div class="navbar"></div><div class="searchbar"></div><div class="page-content"></div><div class="toolbar"></div></div>

Я не могу обернуть дочерние элементы первого уровня (панель навигации, панель поиска) в другой div, у которого нет класса «страница»

У меня есть компонент, который возвращает список строк

для использования в таблице, и я не могу обернуть их в дополнительный тег HTML (не разрешено стандартом HTML5 - я знаю о tbody, но у меня может быть несколько строк, возвращаемых несколькими дочерними компонентами, которые, возможно, потребуется объединить в один tbody ). Была ли кем-нибудь реализована техника, упомянутая @Prinzhorn - обертывание этих дочерних элементов в HTML-комментарий? Я попытался реализовать компонент, который отображает только комментарий HTML, но, похоже, он не работает.

К вашему сведению, переделка, над которой мы работаем (#6170), уже поддерживает фрагменты. Вы можете отслеживать наш прогресс в #7925 и на http://isfiberreadyyet.com.

Сам факт того, что элемент <table> не может отображать определенные элементы, является достаточной причиной для добавления этой функции для обеспечения совместимости с веб-API.

РЕДАКТИРОВАТЬ: Ах, фрагменты! С нетерпением жду их.

@trusktr

Как указано в самом первом сообщении этой темы:

Примечание от сопровождающих:

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

😉

Черт возьми, я должен перестать это делать.

Я проголосовал за это, но хотел поделиться действительным вариантом использования. Предположим, у меня есть 3 кнопки, для которых мне нужен жидкий макет CSS (слева, по центру, справа). Левая кнопка видна всегда, а две другие видны условно. В React (в частности, React Native) я использую его flexbox и визуализирую его как:

[ Left ] { renderCenterAndRightButtons(this.state.someFlag) }

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

Да, я мог бы разделить центральную и правую кнопки на их собственные методы, но это привело бы к созданию большого количества избыточного кода, поскольку они имеют много общего в поведении.

[ Left ] { renderCenterButton(this.state.someFlag) } { renderRightButton(this.state.someFlag) }

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

Я думаю, мы можем закрыть это.

Возврат массивов из компонентов поддерживается начиная с React 16 Beta 1, которую вы можете попробовать прямо сейчас .

Есть еще некоторые ограничения (поддержка SSR не готова), но мы отслеживаем их в #8854 и исправим до финального 16-го релиза.

Спасибо всем за ваши отзывы!

СПАСИБО ДЭН

🍾🍾🍾

🦏

@gaearon Потрясающее улучшение! Поддерживает ли он чистый текстовый узел?

Да, он также поддерживает возвращаемые строки.

Могу я спросить, как это должно работать?

Я надеялся, что это позволит нам визуализировать 2 соседних элемента XJS, но если я сделаю return ["foo", "bar"] (что-то более полезное;), я получу ожидаемый bundle.js:66656 Warning: Each child in an array or iterator should have a unique "key" prop.

Так была ли эта функция способом не окружать реальный список посторонним элементом?

Так была ли эта функция способом не окружать реальный список посторонним элементом?

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

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

@diligiant, возвращающий ['foo', 'bar'] , не имеет отношения к этой проблеме. Вы никогда не могли и до сих пор не можете сделать return <div>{['foo','bar']}</div> Каждый дочерний элемент в массиве должен иметь ключевую опору, будь то во внутреннем теге jsx, таком как <div> , или он возвращается. Что вы можете сделать сейчас:

return [<div key='1'>foo</div>, <span key='2'>bar</span>];

Это снимает большое ограничение в реакции.

Кстати, возврат ['foo', 'bar'] дает вам предупреждение, а не ошибку, и чтобы удалить это предупреждение, вы можете легко присоединиться к этим строкам или, если они являются тегами jsx, а не строками, вы можете добавить к ним опору ключа. Таким образом, нет никаких ограничений на возврат массивов.

@JesperTreetop спасибо.
@sassanh , из вашего примера кажется, что я был слишком «застенчив», чтобы использовать ключи «просто», чтобы избежать окружающего (семантически) бесполезного <div> . Вы имеете в виду, что я должен продолжать и что нет реального штрафа за производительность? Это действительно было бы НАСТОЯЩИМ улучшением!

@diligiant Я сомневаюсь, что это приводит к снижению производительности, но вам не нужен окружающий бесполезный div , если только это не окружающие строки, а если это окружающие строки, вы можете вообще избежать массива и соединить строки. Если это не строки, вы можете добавить ключ к компоненту. Например, если у вас есть два компонента Foo и Bar , вы можете вернуть [<Foo key='foo'/>, <Bar key='bar'/>] , и вам не нужно окружать ни ваши компоненты (как всегда), ни ваш массив (благодаря этот новый выпуск, вам нужно было окружить массив до 16).

@sassanh тогда круто. Конечно, мой вариант использования был с несколькими компонентами. Счастливый!! ;)

На самом деле мне кажется странным, что мы предупреждаем о недостающих ключах, когда все элементы являются строками. Я бы не ожидал, что мы это сделаем. Это существующее поведение в 15, когда строки находятся внутри div? Если нет, то мы должны исправить это в 16 для строк верхнего уровня (поскольку к ним невозможно дать ключи).

@gaearon извините, мой пример ввел в заблуждение: я имел в виду компоненты.

Я проверил и под -beta.5 React не выдает предупреждение при рендеринге массива с несколькими строками.

Тем не менее, меня озадачивает требование key при рендеринге массива только ради того, чтобы избежать окружающего компонента. Я понимаю, и это ничего, но это, вероятно, вызовет массу вопросов по SO (хотя мне нечего предложить лучше…)

И, наконец, еще раз спасибо.

Это предупреждение точно такое же, как и для любого другого использования массивов компонентов, потому что оно сводится к той же «дополнительной работе» для согласователя и, следовательно, к той же самой потенциальной оптимизации путем установки key . Для меня было бы сюрпризом, если бы массивы компонентов должны были обрабатываться по- разному в зависимости от этого, и я полагаю, что это также вызвало бы некоторые вопросы о переполнении стека. Не говоря уже о том, что я думаю, что это потребует некоторых накладных расходов, просто чтобы отслеживать это в первую очередь.

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

Предупреждение необходимо, потому что отсутствие ключей может вызвать проблемы с корректностью, а не только с производительностью. Это объясняется во многих других проблемах, где спрашивают, зачем нужны ключи, поэтому я призываю вас искать их и читать эти обсуждения. Я согласен, что документация могла бы быть более ясной по этому поводу, и мы, вероятно, рассмотрим это в следующий раз, когда будем проводить спринт по изменению документации.

Концептуальных различий между массивами, возвращаемыми непосредственно из рендеринга, и массивами внутри div нет. Таким образом, нет причин, по которым в одном случае будет ключевое предупреждение, а в другом нет. Они должны работать одинаково, потому что оба страдают одними и теми же проблемами, когда ключи отсутствуют.

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

В будущем мы могли бы решить эту проблему, добавив явную поддержку фрагментов в JSX с таким синтаксисом:

return (
  <>
    <div>child 1</div>
    <div>child 2</div>
  </>
);

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

Но у нас пока нет этого синтаксиса. Так что пока это известное ограничение.

@JesperTreetop и @zwily @gaearon объяснили это намного лучше, чем я ;)

Как только вы знаете, это не имеет большого значения, но поскольку мы все хотим, чтобы React процветал, я просто сказал…

@gaearon Есть ли еще одна проблема с предложением по синтаксису <> , которую мы можем посмотреть вместо дальнейшего обсуждения этой проблемы? Я искал вокруг, но я не смог найти ни одного.

@smrq +1 к вопросу — я слежу за всеми этими неуклюжими ограничениями (результат один к одному rendrer() , ключи и синтаксис или фрагменты JSX), но единственный билет, который я знаю, — это https://github.com/ фейсбук/jsx/вопросы/65

Я тоже предполагаю, что файберы вообще решат проблему с ключами — но похоже, что это несбыточная мечта

Ключи - это не "проблема". :) Они являются фундаментальной и необходимой частью любой структуры представления, позволяющей создавать динамические списки.

См. https://facebook.github.io/react/tutorial/tutorial.html#keys для более подробного объяснения.

@spicyj на самом деле это «проблема», поскольку это вызывает дополнительные затраты энергии со стороны разработчиков, и существует принципиальная возможность программировать фреймворк представления без таких требований (например, https://github.com/dfilatov/vidom)

существует принципиальная возможность программировать фреймворк представления без такого требования (например, https://github.com/dfilatov/vidom)

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

@goto-bus-stop vidom может использовать ключи, но они не обязательны и без них только очень большие дела с большим количеством обновлений могут вызвать реальные проблемы с производительностью

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

@veged пример того, как видом борется без ключей .

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

как человек, хорошо знакомый с пространством виртуального дома [1]. я могу сказать, что:

  1. ключи необходимы для предсказуемых обновлений dom и состояния среди похожих братьев и сестер.
  2. keys обычно не являются оптимизацией, а на самом деле обычно наоборот.

[1] https://github.com/leeoniya/domvm

однако однозначная проблема здесь (как описано @gaearon ) - это полностью статическое использование массивов и разница между статическими массивами и статическими фрагментами JSX.

@brigand О, я не сомневаюсь, что изменение порядка компонентов с полным состоянием может вызвать некоторые проблемы ;-), но заставит все остальные случаи (на мой взгляд, их больше) бороться за это ... выглядит спорно

Ключи важны для React. Даже если в некоторых случаях кажется, что они не имеют значения (например, потому что все компоненты ниже не имеют состояния), ничто не мешает вам или кому-то еще в команде добавить компонент с состоянием (или даже простой ввод DOM) где-то несколькими уровнями ниже. через несколько месяцев. К тому времени вы можете забыть, что у вас нет ключей, и, таким образом, изменение порядка может привести к тому, что состояние (или входное значение) будет связано с неправильным элементом.

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

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

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

@smrq создал проблему с предложением для синтаксиса <> в репозитории jsx: https://github.com/facebook/jsx/issues/84.

Мы только что выпустили поддержку нового экспорта React.Fragment и связанного с ним синтаксиса <> :
https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html

Была ли эта страница полезной?
0 / 5 - 0 рейтинги