Комментарии 61
Просто потому что в большинстве случаев нет никакого смысла обращаться напрямую к элементу.
Тем, что язык разметки?
1. синтаксически похож на html;
2. предназначен для описания желаемого DOM — то есть для того же самого для чего предназначен html.
- Не только DOM. Вложенные компоненты тоже описываются как XML-элементы. При желании можно вообще никакого DOM не создавать, а делать всякие sideEffect-ы.
Если вы писали на knockout или angular, то вспомните все эти большие шаблоны с data-bind, v-
& angular attributes. Где таки довольно много логики, бывают фильтры и пр… В запущенном случае там находятся даже двух-трёх этажные js-выражения. И не всегда удаётся провести удачную грань, что тащить в класс, а что оставить в шаблоне. В случае React и JSX, то этот самый шаблон и есть React класс с JSX. Можно воспринимать это как язык шаблонов с расширением js, jsx. JS-шаблонизатор, если хотите.
В то время как логика работы с данными лежит, как правило, отдельно. И там нет JSX. В случае redux это action-ы, actionCreator-ы, reducer-ы, connect-классы, селекторы и пр…
Если вам правда интересно, то вот суть подхода.
Как быть в случае с спагетти от 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 же...
В своих же компонентах вы можете сделать двусторонний биндинг если придумаете как он будет выглядеть.
Есть ли здесь (или около React) что-то напоминающее Binding'и и если есть, то каким инструментом организовано?
Да. Просто пишем что-то вроде onClick="this.props.open"
. В финале это будет addEventListener
, а не аттрибут. Если скажем нужно onChange
на <input/>
или <textarea/>
, то тут начинаются сложности, т.к. никаких удобных механизмов работы с этим из коробки нет. Более того onChange
ведёт себя как onInput
(oO). Есть два подхода для работы с контролами. Оба мне не нравятся. Но, что есть, то есть. В общем дву-направленных bind-ов для работы с контролами тут нет. Из коробки. Люди пишут свои абстракции, либо используют что-нибудь типа redux-form
.
Попробую на примере. Вот есть у вас скажем список чего-то, и есть снизу кнопка "добавить запись". И есть кнопки "удалить" на панели каждого элемента списка. Типичный такой TODO, только ещё проще. В итоге у нас:
- есть презентационные компоненты: Item, List, AddButton. Они с JSX, т.е. там внутри есть XML. Они на входе попросят данные для отображения и 2 функции: для удаления и для добавления. Им "не интересно" ни откуда данные пришли, ни что это за функции. Они просто внутри своего XML пропишут
onClick={this.props.add}
, аналогdata-bind="click: add"
. - у нас есть state. Это древо всех данных приложения. И в нём есть тот самый список. Данные просто данные, т.е. сериализуемые. Никаких функций, хитрых геттеров и сеттеров. Нет, просто тупо данные.
- у нас есть action-ы (опустим для простоты actionCreator-ы). Это такие простые объекты типа
{ type: 'ADD_NEW_ITEM' }
&{ type 'DEL_ITEM', idx: 12 }
. Они сами на данные не влияют. Они просто объекты. Они высказывают намерения. Или как это сказать… Это что-то вроде "эй store, держи мой ADD_NEW_ITEM. обработай его как надо". Причём о store они не знают. Это просто тупо сериализуемые объекты-намерения. Их можно подготовить, к примеру, но не использовать. Просто объекты. - у нас есть reducer-ы. Это чистые функции, которые возьмут старый store, посмотрят на переданный туда action и сделают новый store, но уже с запрошенными изменениями. Вот тут как раз вся бизнес логика по изменению данных приложения. Тут не только JSX нет и в помине, тут вообще весь код… своеобразный. Иммутабельность же.
- у нас есть 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)-а для мелких и средних проектов, чтобы не утонуть в этом бойлерплейте.
Всё вышеописанное лишь моё ИМХО. Никому свою точку зрения не навязываю )
И очень удивлен почему НИ РАЗУ не видел статью на хабре по emberjs. Даже грешным делом сам хотел написать, но все времени нет и т.п.
Vue с компонентным подходом мне больше импонирует (может потому что фарш не устраивает), чем эта модная «jQuery лапша» именуемая гордо РЕАКТ и JSX.
Первое — там обычный «хеллоу ворлд» который можно почитать и на самом сайте (ну может у вас чуть другой).
Я планировал статью немного другого плана. Типа как у Symfony раньше была — «Зачем нужен фреймфорк».
Ну и самое главное не упомянули про emberobserver.com
А я например через него узнал про такую замечательную штуку как PouchDB (искал адаптеры для баз).
И этот подход по мне правильный по части компонентов — автономный кусок.
мешанина хтмл с жс
Легко обозвать незнакомую технологию мешаниной и тем самым оправдать своё невежество.
Гораздо сложнее понять, как так вышло, что 3 года назад такой подход появился и до сих пор успешно используется.
Гораздо сложнее понять что существуют другие, более удобные технологии.
Но это же реакт и фасебук да?
Легко обозвать незнакомого человека невеждой, познав одну технологию
До Реакта я писал на Marionette и Angular. Со знанием альтернативных технологий у меня все в порядке.
Но это же реакт и фасебук да?
А Angular это Google. Но почему-то не так успешно летит Angular, несмотря на поддержку большой компании.
onClick={this.handleClick.bind(this)}
бинд внутри рендера делать категорически не стоит.
.bind
порождает новую функцию. Это всегда будет новый экземпляр. В данном случае это будет происходить при каждом вызове .render()
. И тот компонент которому этот onClick=
назначен всегда будет получать новый экземпляр onClick
в своих props
. И если такой компонент это pureComputed
то он будет всегда заного рендерить свой virtualDom. Без разбору, всегда. Надо не надо… будет. Потому что shallow
-проверка будет сверять по ===
.
Функции можно писать и так:
const OurFirstComponent = () => { return ( // То, что нужно для создания компонента, идёт сюда ); }
stateless компоненты можно писать с еще чуть меньшей вложенностью)
const OurFirstComponent = () => (
<h1>Hello world</h1>
);
Уважаемые читатели! Если сегодня состоялось ваше первое знакомство с React — просим поделиться впечатлениями.
Оптимистичное заявление на Хабре ;)
Чтобы не разочаровывать топикпарсера отвечаю…
Я не профессиональный программист. Я педагог преподающий в кружке информатику. И когда встал вопрос про выбор между Angular, React и Vue, то я остановился на Vue.
Почему? Как мне показалось Vue код ближе всего к JavaScript и HTML (с уклоном в HTML). Причем не смешивает оно и другое. Меньше нужно какие то места объяснять «так принято», «они решили сделать по другому», «это МАГИЯ» и пр.
Хотя React, бесспорно, тоже просмотрю. Говорят с React 16 там лицензия MIT. А это было одним из минусов, которые подтолкнули меня делать курс не на нем.
Как плюс React могу отметить большое количество руководств как сделать ToDo
читая хабр я знаю, что тут умные люди. Поэтому вчитываясь в очепятку я думаю, что так и задумано, перепрочитывая фразу несколько раз
vue — это хорошо и приятно, но вакансий пока больше на реакте
Уважаемые читатели! Если сегодня состоялось ваше первое знакомство с React — просим поделиться впечатлениями.
1е знакомство. Вроде и понятно, но недосказанности.
Как селекторы под css добавлять после по такому коду jsx — удобно?
Отсюда: получается это нечто temlate-ора для одной-пары уникальных страниц на сайте?
В чем глобальное преимущество над js или jquery?
Совсем непонятно
Эта конструкция сложнее, но она необходима только в том случае, если вы используете старое состояние для формирования нового состояния. Если нет — можно просто передавать setState объект.это про (if) a = !a;?
Отличия view-библиотеки от шаблонизатора — работа на уровне объектов, а не на уровне строк. Соответственно, нет необходимости бояться инъекций и задумываться над экранированием. Плюс обработчики событий можно привязывать напрямую, а не после рендера с поиском элемента по какому-нибудь id. Но если все это отбросить — то да, это по сути шаблонизатор.
это про (if) a = !a;?
Да, именно эта операция тут так делается. Но есть библиотеки которые позволяют делать ее проще (mobx-react).
В получении дом-элемента по селектору обычно необходимости нет, собственно когда кажется, что она есть, то велика вероятность, что что-то сильно не так делаешь.
Если сегодня состоялось ваше первое знакомство с React — просим поделиться впечатлениями.
Несколько месяцев назад моя компания оплатила мне курсы реакта. Чтож, фреймворк создаёт очень приятные впечатления возможностью невероятно быстро стартануть/запрототипировать проект и наличием огромного комьюнити. А редакс оказался настолько просто и хорош собой что мы стали использовать его на Angular 2 проекте (ngrx/store, если быть точным).
Многие в комментариях жалуются на смешение HTML и JS кода в одной функции. Но глядя на любой проект в котором я участвовал, я вижу либо пилёж логики в самом шаблоне, либо ад из дата-атрибутов. Для себя я решил относиться к JSX коду как как к view-слою, который легко протестировать и понять и меня отпустило. Даже понравилось, если честно.
Такие дела.
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);
}
Основы React: всё, что нужно знать для начала работы