Pull to refresh

Comments 37

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

Для причёсывания этих проблем нам нужны или расширения языка шаблонов, более крутые, чем имеющееся поколение в виде JSX, tagged template literals и т.д, или вообще принципиально другой шаблонизатор, в котором вопросы привязки данных будут решаться сразу (типа $mol, только написанный для людей).
UFO just landed and posted this here
UFO just landed and posted this here
Спасибо за детальный комментарий.

По первому пункту — я говорю именно о реализации во Vue и WebComponents, хотя я возможно недостаточно ясно это написал.
По второму и третьему — тут у нас небольшое расхождение понятий. Слоты — не неймспейсы, а определения заранее того, куда именно будет прокинут компонент. У тебя в проекте в чилдрены можно пробросить что угодно, а слоты именно о том, что Start всегда будет в начале, а End — всегда в конце, и их нельзя поменять местами, но можно, например, подставлять что-нибудь по умолчанию, если End — нет (на базовом уровне).

По вопросу «спускания» данных, в твоем проекте, насколько я понимаю, у тебя все данные и действия с ними идут через внешний менеджер состояния, redux (что я описал в №3), поэтому мне сложно это сопоставить с подходом №4.
UFO just landed and posted this here

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


  • "например, текущий аутентифицированный пользователь, UI-тема или выбранный язык"

То есть как достаточно ограниченный инструментарий для задач именно по контексту, а не "исчерпывающие решения по управлению состоянием приложения"


Из этого следует, что:


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

То есть вопрос именно о логике и данных, а не "про организацию компонентов".


Естественно, тут обсуждается написание react.js-приложений с этими аксиомами, но есть и другие подходы.


Также, ты не совсем прав, в твоём приложении логика вынесена вместе с данными, например, resetTasksList и unshareTask. И было бы странно оставлять это в компоненте, а не энкапсулировать это вместе с данными, если использовать методологию ООП.

UFO just landed and posted this here
UFO just landed and posted this here
  • render props — частный случай слотов
  • React.Children.map
Если честно, я так и не понял проблем, о которых ты говоришь. Вот компонент (а вот ещё) с моего недавнего проекта, я никаких проблем со спусканием данных даже близко не ощущал, и все, что идёт до 4-го пункта — это, если честно, для меня какой-то не понятно, чем вызванный, code smell.

Если вы приводите в пример код компонента, в котором нужно сначала сильно так промотать вниз, чтоб найти собственно сам компонент, потому что перед ним такой толстый слой обёрточных типов и кода; а потом еще и в шаблоне без поллитры не разобраться, потому что он представляет из себя сплошной тернарный оператор — то чёт мне кажется, что вы пропустили очень-очень много иронии в вашем комментарии, написав «не понял проблем».

Вы серьезно не видите проблем в том, что о назначении вашего компонента можно догадаться либо по его имени, либо после доброго часа вникания в его код?
UFO just landed and posted this here

В вашем компоненте больше половины — типовой код, который следовало бы вынести в отдельные абстракции ( в том числе и в отдельные компоненты), а не копипастить его из компонента в компонент.

UFO just landed and posted this here

Вас самого-то не смущает, что в коде вы 8 раз ветвитесь по searchOpened в каждой ветке копипастя одну и ту же логику? А ручной менеджмент этих бесконечных loading? А ручная реализация очередного переключателя и одинаковой логики переключения страниц?

UFO just landed and posted this here
покажешь готовый аналог, выполняющий те же задачи и более кратко — я буду только счастлив

mol.js.org


какую? Строку скажи, мне самому интересно, где у меня там копипаста

ctrl+f => searchOpened

UFO just landed and posted this here

Выставляете себя ещё большим дураком такими комментариями.

UFO just landed and posted this here
UFO just landed and posted this here
function SplitPane(props) {
  return (
    <div className="SplitPane">
      {
      props.left &&
      <div className="SplitPane-left">
        {props.left}
      </div>
     }
     {
      props.right &&
       <div className="SplitPane-right">
        {props.right}
       </div>
     }
    </div>
  );
}

Для сравнения то же самое на view.tree:


$my_split_pane $mol_view
    sub /
        <= Left $my_view sub <= left /
        <= Right $my_view sub <= right /

И примеры использования:


OnlyContacts $my_split_pane
    left <= contacts_views /
    Right null

OnlyChat $my_split_pane
    Left null
    right <= message_views /

FullView $my_split_pane
    left <= contacts_views /
    right <= message_views /
Мне кажется, ваша основная проблема в том, что вам просто чисто эстетически не нравится передавать компоненты в виде props.
А этот код вызывают у вас как минимум раздражение
function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}


Но в случае с вашим же примерном со слотами
function App() {
  return (
    <SplitPane>
      <LeftPane>
        <Contacts />
      </LeftPane>
      <RightPane>
        <Chat />
      </RightPane>
    </SplitPane>
  );
}


1. А что если кто-то будет пере-использовать компонент SplitPane и поменяет местами LeftPane и RightPane? В случае float верстки это может быть критично.

2. А если в корень SplitPane засунуть другой компонент или div?

3. Мне кажется, это изобретение очередного table — сурового и беспощадного со своей спецификацией.

А если переписать так?



В моем приложении у меня как раз если список чатов и список контактов. И я использовал тот же подход что и вас «паттерн №5 – слоты». Только в моем случае SplitPane — это компонент обертка над css-table, а LeftPane и RightPane — обертка над css-td. Дочерних компонентов может быть или один или два и они даже могут быть изменены местами. В других случаях паттерн №5 мне кажется избыточен.

Именно, меня раздражает "ад оберток", так как это разрушает достаточно удобный и интуитивный для меня формат — JSX — и заставляет либо выносить даже небольшие кусочки JSX в отдельные JS-переменные, либо разбираться в уровнях вложения фигурных скобок.


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


То есть, leftpane и rightpane вставлялись бы так же как и с композицией, только в одном месте, и их нельзя было бы поменять местами или использовать для них другой компонент (не используя override, который может изначально быть не включаемым по решению создателя слота).


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

wrapped-hell — «ад оберток» — это вот:
image

Если в SplitPane использовать left и right в виде props то будет один уровень вложенности. В вашем случае уровней будет два. И где тут решение проблемы «ад оберток»?
Действительно, я имел в виду именно неудобный синтаксис. Однако уровень вложенности в devtools все равно будет один, так как это те же пропсы, просто выраженные внутри пропса children, а не как отдельный пропс. Опять же, если использовать возможности React API, появляется достаточно богатый выбор сценариев рендера — слот может быть и контейнером стилей и сам может быть компонентом, который можно при желании подменять в манере DI, или просто быть указателем того, куда рендерятся его children (и если их множество, то как и в случае с пропсом нужна либо обертка Fragment (что в слот можно подставлять автоматически, а в пропс нужно писать руками), либо map-функция в рендере дочернего компонента).

Между render-props и children-slots разница больше визуальная. По сути этот 1 и тот же подход. Вы так или иначе передаёте это через props. В случае render-props напрямую, в случае children-slots через children property в виде древа. В отличии от Vue (наверное) в React есть некоторые проблемы с обработкой children на нижележащих слоях — вы вынуждены искать нужные вам компоненты чтобы обходиться с ними как со слотами. И это препятствует тому, чтобы можно было их как-нибудь обернуть своим компонентом. Не то чтобы это всегда сильно было нужно. Но когда в очередной раз натыкаешься на эту родовую травму React хочется в очередной раз отказаться от любых абстракций на уровне children.

Красота JSX это по сути синтаксический сахар. А вы начинаете городить из этого свой велосипед.
const element = (
  <h1 className="greeting">
    Привет, мир!
  </h1>
);
// или
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Привет, мир!'
  }
};
— это ведь одно и тоже!
Однако уровень вложенности в devtools все равно будет один, так как это те же пропсы, просто выраженные внутри пропса children, а не как отдельный пропс
в вашем случае будет один лишний уровень вложенности для LeftPane и для RightPane — проверьте, если не верите.
выносить даже небольшие кусочки JSX в отдельные JS-переменные
все правильно, так и нужно делать
Действительно, я имел в виду именно неудобный синтаксис
я использую Visual Studio Code с prettier по нажатию save файла. Этого вполне достаточно чтобы не путаться в скобочках
выносить даже небольшие кусочки JSX в отдельные JS-переменные
все правильно, так и нужно делать
Тяжелый случай.
Разбивать код на части следует, если от этого есть польза. Иначе это лишь ухудшит читаемость кода.

А если переписать так?
image
В таком маленьком куске кода создается ошибочное ощущение, что все хорошо.
Когда компоненте будет хотя бы 50 строк, а SplitPane будет между другими компонентами (например, пара десятков div-ов над ним и под ним), то уже будет ухудшена читаемость кода. Т.к. тесно связанные SplitPane, contacts, chat отделены друг от друга. В случае слотов такого не будет.
Автор публикации не смог предоставить каких либо аргументов в качестве доказательства удобства теории слотов. Я нашел его репозитарий на гитхабе там нет оценок кроме его собственной. Кроме того, после
моего примера
const element = (
  <h1 className="greeting">
    Привет, мир!
  </h1>
);
// или
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Привет, мир!'
  }
};
— это ведь одно и тоже!
автор вообще не смог ничего ответить. Видимо, это связано с поверхностными знаниями в том как работает React и как код трансформируется в JSX.

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

image

Я использую Babel вместе с Webpack для компиляции кода, и там есть встроенные механизмы оптимизации которые уберут мои созданные переменные и подставят коды компонентов в нужные места.

Вы сами используете эти слоты?
Я не согласен с автором статьи, что «избавление от ада оберток» является целью слотов. Они для того же, для чего и render props, только с той разницей, что JSX код содержимого компонента не выносится вне этого компонента. Да, добавляется дополнительный слой вложенности. Но логически понятный. Что мы видим в редакторе, то же отображается в devtools. Так что не вижу в этом особой проблемы.

Я не использовал слоты. В реакте их нет. А реализовывать самому неохота.
Использовал только сторонние компоненты со слотами вроде такого: react-bootstrap.github.io/components/modal/#modals-live
Я где то выше писал:
3. Мне кажется, это изобретение очередного table — сурового и беспощадного со своей спецификацией.
а тут вы как раз кидаете пример и там прям так все и расписано. В случае с bootstrap 4 очень даже не плохо выглядит, Хотя статус беты немного отпугивает и то что нужно еще учить документацию по тому как bootstrap обернут в слоты.
Ну а что поделать. Если из коробки нет нужного функционала и общепринятых решений тоже нет, каждый решает свои проблемы как может.

После упоминания про beta я понял, что использовал reactstrap, а не react-bootstrap)
reactstrap.github.io/components/modals
2. Необязательность означала бы перегружение компонента SplitPane логикой
4. Эту логику отображения пришлось бы писать заново для каждого компонента, принимающего пропсы.

Эти проблемы можно решить просто введя еще одну обертку:
function SafeDiv({children, ...props}) {
    return (
        children ?
            <div {...props}>
                {children}
            </div>
        :
            null
    );
}

Теперь можно смело писать:
function SplitPane(props) {
    return (
        <div className="SplitPane">
            <SafeDiv className="SplitPane-left">
                {props.left}
            </SafeDiv>
            <SafeDiv className="SplitPane-right">
                {props.right}
            </SafeDiv>
        </div>
    );
}
Sign up to leave a comment.

Articles

Change theme settings