Pull to refresh

Comments 61

Ну, для начала JSX не «язык программирования». Реакт знаю плохо, прав ли я, что ref плохой способ обратиться к элементу, и имеет место лишь в определенных случаях? (я понимаю, что тут пример для ньюби и это просто пример, потому он и указан). Если прав, то было бы круто услышать об иных способах. Ну и ждём-с статью про подробный разбор состояний, поднятие, редакс и т.п.
ref — канонический способ обратиться к элементу. Но, конечно же, число случаев когда его следует использовать — ограничено.

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

В статье ref используется только в одном месте — для работы с <audio>. Это как раз тот самый случай когда без него не обойтись.

Чем JSX не язык программирования?
Тем же, чем и тайпскрипт. В общем-то вы и сами на свой вопрос отвечали ниже…

JSX — надмножество JS, это не XML-подобный язык, а JS c возможностью включать XML-подобный синтаксис для некоторых целей, например с целью трансляции в вызовы React.createElement() или любые другие на ваше усмотрение.

У вас все языки, не полные по Тьюрингу, — не языки?
Единственное, что меня смущает в туториалах реакта, это тотальное смешивание html и js кода. Разве это не считается bad practice? Можно ли обойтись совсем без смешивания одного с другим или это неотъемлемая часть JSX?
Как бы JSX — это и есть почти-html внутри js.
Да срань это господня, простите.
UFO just landed and posted this here
Тем не менее, он:

1. синтаксически похож на html;
2. предназначен для описания желаемого DOM — то есть для того же самого для чего предназначен html.
  1. Не только DOM. Вложенные компоненты тоже описываются как XML-элементы. При желании можно вообще никакого DOM не создавать, а делать всякие sideEffect-ы.
Формально, JSX — это надмножество, расширение JavaScript, дополняющего его возможностью использовать XML-подобный синтаксис для некоторых целей.

Если вы писали на knockout или angular, то вспомните все эти большие шаблоны с data-bind, v-
& angular attributes. Где таки довольно много логики, бывают фильтры и пр… В запущенном случае там находятся даже двух-трёх этажные js-выражения. И не всегда удаётся провести удачную грань, что тащить в класс, а что оставить в шаблоне. В случае React и JSX, то этот самый шаблон и есть React класс с JSX. Можно воспринимать это как язык шаблонов с расширением js, jsx. JS-шаблонизатор, если хотите.


В то время как логика работы с данными лежит, как правило, отдельно. И там нет JSX. В случае redux это action-ы, actionCreator-ы, reducer-ы, connect-классы, селекторы и пр…
Если вам правда интересно, то вот суть подхода.

Мы используем knockout. Да, data-bind'ов очень много. Я бы даже сказал что на этом все и построено. Но! Мы можем отделить ViewModel от View и от Model. У нас одна и та же ViewModel может быть привязана к разным View. И это работает.

Как быть в случае с спагетти от React?
Как отделить этот HTML-не HTML от бизнес логики? Как сделать так, чтобы одни и те же данные отображались по разному в разных частях общей View (компонента если хотите)?

В большинстве больших приложений с React используется внешний state manager. Например Redux или MobX. Данные хранятся там. Взаимодействие с ними хранится где либо ещё. В случае redux работа с данными и вообще вся не UI логика лежит во множестве слоёв: action-ы, actionCreator-ы, reducer-ы, selector-ы, container-компоненты. И там нет JSX. Как правильно это самостоятельные JS файлы без единой XML строчки.


А React компонент представляет из себя уже UI часть для этих данных. Он их получает свыше. Менять он их не умеет, но умеет, гхм, дёргать рычаги, которые приведут к их изменению. Опять же, рычаги эти проброшены сверху. В общем никакой прямой обработки данных. Что-то вроде шаблонизатора.


^ всё это немного упрощённо, лишь по сути. И да, перейдите по моей ссылке. Многое станет понятнее, я думаю.

Не спора ради, а разобраться для…
Правильно ли я понимаю что React это инструмент для создания только View. За остальное (если здесь уместно говорить про MVVM, то VM и M) отвечают другие инструменты. Есть ли здесь (или около React) что-то напоминающее Binding'и и если есть, то каким инструментом организовано?
Есть ли здесь (или около React) что-то напоминающее Binding'и и если есть, то каким инструментом организовано?

Так JSX же...

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

В своих же компонентах вы можете сделать двусторонний биндинг если придумаете как он будет выглядеть.
Двустороннего нет и обычно попытки его как-то неявно реализовать считаются антипаттерном в мире реакта.
Есть ли здесь (или около React) что-то напоминающее Binding'и и если есть, то каким инструментом организовано?

Да. Просто пишем что-то вроде onClick="this.props.open". В финале это будет addEventListener, а не аттрибут. Если скажем нужно onChange на <input/> или <textarea/>, то тут начинаются сложности, т.к. никаких удобных механизмов работы с этим из коробки нет. Более того onChange ведёт себя как onInput (oO). Есть два подхода для работы с контролами. Оба мне не нравятся. Но, что есть, то есть. В общем дву-направленных bind-ов для работы с контролами тут нет. Из коробки. Люди пишут свои абстракции, либо используют что-нибудь типа redux-form.

Попробую на примере. Вот есть у вас скажем список чего-то, и есть снизу кнопка "добавить запись". И есть кнопки "удалить" на панели каждого элемента списка. Типичный такой TODO, только ещё проще. В итоге у нас:


  1. есть презентационные компоненты: Item, List, AddButton. Они с JSX, т.е. там внутри есть XML. Они на входе попросят данные для отображения и 2 функции: для удаления и для добавления. Им "не интересно" ни откуда данные пришли, ни что это за функции. Они просто внутри своего XML пропишут onClick={this.props.add}, аналог data-bind="click: add".
  2. у нас есть state. Это древо всех данных приложения. И в нём есть тот самый список. Данные просто данные, т.е. сериализуемые. Никаких функций, хитрых геттеров и сеттеров. Нет, просто тупо данные.
  3. у нас есть action-ы (опустим для простоты actionCreator-ы). Это такие простые объекты типа { type: 'ADD_NEW_ITEM' } & { type 'DEL_ITEM', idx: 12 }. Они сами на данные не влияют. Они просто объекты. Они высказывают намерения. Или как это сказать… Это что-то вроде "эй store, держи мой ADD_NEW_ITEM. обработай его как надо". Причём о store они не знают. Это просто тупо сериализуемые объекты-намерения. Их можно подготовить, к примеру, но не использовать. Просто объекты.
  4. у нас есть reducer-ы. Это чистые функции, которые возьмут старый store, посмотрят на переданный туда action и сделают новый store, но уже с запрошенными изменениями. Вот тут как раз вся бизнес логика по изменению данных приложения. Тут не только JSX нет и в помине, тут вообще весь код… своеобразный. Иммутабельность же.
  5. у нас есть container-компоненты. Они имеют связь со store. По сути store это ko.observable. И container-компоненты на него subscribe-ны. Если store меняется, то они получают новые данные store из него. Их задача — получить данные из store, выдрать из них только то, что нужно конкретно взятому компоненту из пункта 1, и подготовить все эти add и delItem методы для него же. Эти add и delItem-ы опять же, будут просто толкать в store (в его reducer) все те action-ы из пункта 3. Т.е. и тут бизнес логики практически нуль. Подготовив все эти данные container-компонент передаёт их своему приемнику, т.е. презентационному компоненту.

Наверное сильно запутал. Ну там всё не настолько просто, чтобы в паре предложений описать. В общем суть — нет в больших React приложениях никакого нагромождения XML внутри JS, так, чтобы бизнес-логика и вьюхи были рядом. Ещё суть — если вы бросите knockout и возьмёте стандартную связку react + redux, то последнее, что станет для вас проблемой, это этот несчастный JSX. Мат трёхэтажный у вас будет стоять от огромного количества бойлерплейта (мне кажется порой раз в 15 больше суеты чем в knockout-е), и вырвиглазной (имхо) работы с immutable значениями (ну не haskell у нас, не haskell, как умеем, как могём). А когда что-нибудь начнёт тормозить, то пойдут и нормализация данных в store, и selector-ы (мемоизированные функции) и прочая чертовщина. В общем прямолинейный в knockout.js js-код будет разбросан по десяткам файлов и вообще даже близко не будет напоминать обычное словесное описание того, что вам нужно. Тут конвеерная несколькозвенная обработка изменения иммутабельного состояния. Никаких больше: this.list.add(newItem). Теперь это будет через карусель: actionCreator -> action -> reducer -> mapStateToProps -> view.


Из плюшек же: всё работает очень предсказуемо и отслеживаемо. Вот прямо до предела. Вплоть до того, что можно перезагружать страницу целиком и на экране ничего не поменяется, т.к. данные в store те же (скажем localStorage) и всё остальное лишь чистые функции по работе с ними. Тестируется опять же на раз два.


У меня бывало уходили часы работы на поиск хитрого бага в сложном knockout приложении (3-4 года работы в одно рыло, большая кодовая база, много legacy). Потому что stack-трейсы для асинхронных observable бесполезны. А если там 1 computed зависит от 2-го, а 2-й от 3-го и они ещё разных типов, то во-первых — это всё работает непредсказуемо и ну очень непонятно. Последовательность пересчёта всего этого клубка (а knockout располагает к сложным клубкам) может быть разной в зависимости от нюансов. Сами эти связи могут быть совершенно не очевидными, стоит только отвлечься и забыть контекст. Никогда нельзя сказать, что вот тут у нас сложный баг, давайте просто посмотрим что от чего зависит… потому что это может быть ну очень непросто и в каждом модуле устроено по своему. Вспоминаю сейчас и испарина на лбу.


Поработав 1 год на redux+react и 3 на knockout, я бы для больших проектов выбирал react+redux, т.к. такой проект спустя годы будет иметь меньше технических долгов и куда проще поддерживаться. А vue (вместо knockout)-а для мелких и средних проектов, чтобы не утонуть в этом бойлерплейте.


Всё вышеописанное лишь моё ИМХО. Никому свою точку зрения не навязываю )

Спасибо. Этот комментарий рассказал больше чем сама статья.
Комментарий рассказывает в основном не о реакте, а о редаксе :)
React — это библиотека для построения View и только View. Для других частей предполагается использовать другие библиотеки.
JSX и есть возможность смешивать JS и XML. Вы сами определяете, где смешивать, а где нет. Это с одной стороны. С другой, в props и context (что-то вроде DI-контейнера) компоненту можно передавать любое JS значение, в том числе полноценные объекты и функции, реализующие любые сайд-эффекты, используя компоненты в качестве тупых шаблонизаторов. .state не является обязательным для использования, в некоторых практиках его использование вообще запрещено. Но ответственность за своевременный ререндеринг тогда полностью ложится на вас.
Честно говоря не понимаю хайпа вокруг реакта. Для меня мешанина хтмл с жс, называемая «правильным подходом» как то отталкивает.
И очень удивлен почему НИ РАЗУ не видел статью на хабре по emberjs. Даже грешным делом сам хотел написать, но все времени нет и т.п.

Vue с компонентным подходом мне больше импонирует (может потому что фарш не устраивает), чем эта модная «jQuery лапша» именуемая гордо РЕАКТ и JSX.
Пропустил. Просто в глазах РЯБИТ от этого реакта на хабре
К сожалению не могу комментить там.
Первое — там обычный «хеллоу ворлд» который можно почитать и на самом сайте (ну может у вас чуть другой).
Я планировал статью немного другого плана. Типа как у Symfony раньше была — «Зачем нужен фреймфорк».
Ну и самое главное не упомянули про emberobserver.com
А я например через него узнал про такую замечательную штуку как PouchDB (искал адаптеры для баз).
Во Vue тоже мешанина, только в другую сторону: код на js пишется прямо в html-файлах. :-(
Не, там хотя бы разделяешь <template> <script> и <style>
И этот подход по мне правильный по части компонентов — автономный кусок.

О разном вы говорите. Я думаю mayorovp говорит про все "v-" аттрибуты. Типичный презентационный React компонент с JSX это то, что во vue лежит внутри template.

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

Легко обозвать незнакомую технологию мешаниной и тем самым оправдать своё невежество.


Гораздо сложнее понять, как так вышло, что 3 года назад такой подход появился и до сих пор успешно используется.

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

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

До Реакта я писал на Marionette и Angular. Со знанием альтернативных технологий у меня все в порядке.


Но это же реакт и фасебук да?

А Angular это Google. Но почему-то не так успешно летит Angular, несмотря на поддержку большой компании.

Я использовал многие технологии на фронте за 20 лет в веб-разработке. Лично мне React кажется наиболее удобным.
плохой совет:
onClick={this.handleClick.bind(this)}

бинд внутри рендера делать категорически не стоит.
UFO just landed and posted this here

.bind порождает новую функцию. Это всегда будет новый экземпляр. В данном случае это будет происходить при каждом вызове .render(). И тот компонент которому этот onClick= назначен всегда будет получать новый экземпляр onClick в своих props. И если такой компонент это pureComputed то он будет всегда заного рендерить свой virtualDom. Без разбору, всегда. Надо не надо… будет. Потому что shallow-проверка будет сверять по ===.

UFO just landed and posted this here
Я толерантный ко всем фреймворкам: хорошим, не очень, React.
Функции можно писать и так:

const OurFirstComponent = () => {
  return (
    // То, что нужно для создания компонента, идёт сюда
  );
}



stateless компоненты можно писать с еще чуть меньшей вложенностью)

const OurFirstComponent = () => (
    <h1>Hello world</h1>
);

Ваш случай можно ещё больше упростить и слегка ускорить :)


const helloWorld = <h1>Hello, World</h1>; // declare
// ...
{helloWorld} // use
Уважаемые читатели! Если сегодня состоялось ваше первое знакомство с React — просим поделиться впечатлениями.

Оптимистичное заявление на Хабре ;)

Чтобы не разочаровывать топикпарсера отвечаю…
Я не профессиональный программист. Я педагог преподающий в кружке информатику. И когда встал вопрос про выбор между Angular, React и Vue, то я остановился на Vue.
Почему? Как мне показалось Vue код ближе всего к JavaScript и HTML (с уклоном в HTML). Причем не смешивает оно и другое. Меньше нужно какие то места объяснять «так принято», «они решили сделать по другому», «это МАГИЯ» и пр.

Хотя React, бесспорно, тоже просмотрю. Говорят с React 16 там лицензия MIT. А это было одним из минусов, которые подтолкнули меня делать курс не на нем.
Как плюс React могу отметить большое количество руководств как сделать ToDo
Классы компонентов и функциональных компоненты

читая хабр я знаю, что тут умные люди. Поэтому вчитываясь в очепятку я думаю, что так и задумано, перепрочитывая фразу несколько раз
UFO just landed and posted this here
Вчера только начал реакт тыкать. И тут прям подарок — статья на хабре. Спасибо
vue — это хорошо и приятно, но вакансий пока больше на реакте
Уважаемые читатели! Если сегодня состоялось ваше первое знакомство с React — просим поделиться впечатлениями.

1е знакомство. Вроде и понятно, но недосказанности.
Как селекторы под css добавлять после по такому коду jsx — удобно?
Отсюда: получается это нечто temlate-ора для одной-пары уникальных страниц на сайте?
В чем глобальное преимущество над js или jquery?
Совсем непонятно
Эта конструкция сложнее, но она необходима только в том случае, если вы используете старое состояние для формирования нового состояния. Если нет — можно просто передавать setState объект.
это про (if) a = !a;?

Отличия view-библиотеки от шаблонизатора — работа на уровне объектов, а не на уровне строк. Соответственно, нет необходимости бояться инъекций и задумываться над экранированием. Плюс обработчики событий можно привязывать напрямую, а не после рендера с поиском элемента по какому-нибудь id. Но если все это отбросить — то да, это по сути шаблонизатор.


это про (if) a = !a;?

Да, именно эта операция тут так делается. Но есть библиотеки которые позволяют делать ее проще (mobx-react).

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

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

Несколько месяцев назад моя компания оплатила мне курсы реакта. Чтож, фреймворк создаёт очень приятные впечатления возможностью невероятно быстро стартануть/запрототипировать проект и наличием огромного комьюнити. А редакс оказался настолько просто и хорош собой что мы стали использовать его на Angular 2 проекте (ngrx/store, если быть точным).


Многие в комментариях жалуются на смешение HTML и JS кода в одной функции. Но глядя на любой проект в котором я участвовал, я вижу либо пилёж логики в самом шаблоне, либо ад из дата-атрибутов. Для себя я решил относиться к JSX коду как как к view-слою, который легко протестировать и понять и меня отпустило. Даже понравилось, если честно.


Такие дела.

Я только начал изучать React. И ваше руководство как нельзя кстати. Очень кратко и самое необходимое. Большое спасибо. Планируется ли вторая часть?
Решение без использования JSX и Babel соответственно:

index.html
<link rel="stylesheet" href="app.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,800">
<div id="container">
  <div id="hook"></div>
  <h1>Play Music</h1>
  <input type="file" id="files" name="files[]" multiple />
</div>
<script crossorigin src="https://unpkg.com/react@15/dist/react.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
<script src="https://soulwire.github.io/sketch.js/js/sketch.min.js"></script>
<script src="app.js"></script>
<script type="text/javascript">
  'use strict';
  function PlayButton(props) {
    let className = props.isMusicPlaying ? 'play active' : 'play';
    return React.createElement('a', {href: "#", title: "Play video", onClick: props.onClick, "className": className}, null);
  }
  class Container extends React.Component{
    constructor(){
      super();
      this.state = {isMusicPlaying: false};
    }
    handleClick() {
      this.setState({ isMusicPlaying: !this.state.isMusicPlaying });
      this.state.isMusicPlaying ? this.audio.pause() : this.audio.play();
    };
    render(){
      let status = this.state.isMusicPlaying ? 'Playing :)' : 'Not playing :(';
      
      return React.createElement('div', null,
              React.createElement('h1', {onClick: this.handleClick.bind(this)}, status),
              PlayButton({isMusicPlaying: this.state.isMusicPlaying, onClick: this.handleClick.bind(this)}),
              React.createElement('audio', {id: "audio", ref: (audioTag) => { this.audio = audioTag } })
             );
    }
  }
  const placeWeWantToPutComponent = document.getElementById('hook');
  ReactDOM.render(React.createElement(Container), placeWeWantToPutComponent);
</script>

Тогда уж лучше добавить react-dom-factories и получить возможность создаввать элементы простыми вызовами функций.


const { a } = ReactDOMFactories;
function PlayButton(props) {
    const className = props.isMusicPlaying ? 'play active' : 'play';
    return a({href: "#", title: "Play video", onClick: props.onClick, className: className}, null);
}
Sign up to leave a comment.