Comments 66
Ох, сколько же бывалых веб-разработчиков оттолкнула каша из HTML-тегов внутри JavaScript-кода. У меня аллергия прошла через три года, а тут ещё ES6 подоспел — кошмар ретрограда. Но всё к лучшему, за это время экосистема React-а выросла и окрепла. Как раньше люди жили без Redux-а, наверно мучились. :)
В том то и дело, что каша из кода и тегов — это далеко не серебрянная пуля, как бы React не был хорош. Да, модно, да, решает задачу, но всегда найдется альтернативный подход, а у него — сторонники.
Беда в том, что нет единственно правильного ответа. Пока React победил в моих изысканиях. Предыдущая ставка на Meteor — потерянное время.
С нетерпением ждём следующей серии в изысканиях :-)
Слишком интимный вопрос, многих ранила моя статья эту тему. Отвечу в личку.
Хотя один камрад стойко преодолевает трудности.
Вот это самая лучшая статья по React+Redux из тех, что я перелопатил. Три раза перечитывал. И постиг Дзен.
Ещё хочу дать самые лестные рекомендации проекту Monster Lessons: JavaScript, React, Redux. Превосходная подача материала!
У меня был опыт работы с метеором только на внутренних проектах, и ни разу не разочаровался в нем. Из примеров — это приложение, интегрированное с основным проектом на рельсах, созданное для совместной обработки заказов пользователей, и приложение для администрирования нескольких игровых серверов (mongodb).
Мне понятно, что примеры достаточно специфичные, также стоит учитывать, что я писал их один. И также я представляю, что с проектами на метеоре может быть множество проблем. Но категорически не могу согласиться с проведением параллелей с React, эти инструменты решают разные задачи, причем могут это делать совместно, по моему мнению, метеор — это в первую очередь протокол клиент-серверного общения.
Из того, что мне очень нравится в метеоре: он дает высокую скорость при создании скелета приложения, дает возможность писать минимум серверного кода (и, как правило, синхронного кода) и дает данные в реальном времени на клиенте. Из специфичного, мне нравится blaze, и вообще, то как реализована реактивность на фронтэнде. Из главных минусов, по моему мнению: это то что все завязано на монге и ddp, и, как следствие, узкая область применимости, но это и есть суть фреймворка. Я не считаю, что клиентская часть приложения может быть проблемой в принципе, так как есть полная свобода в выборе инструментов, но, конечно, есть свои особенности, вытекающие из протокола. Также я не могу судить о возможных проблемах, связанных с высокой нагрузкой, но судя по различным публикациям в интернете — дела обстоят более-менее, и это уже индивидуальный вопрос для каждого проекта.
Сам бы я решился начать новый проект на метеоре, если нужны данные в реальном времени на клиенте, если не нужна сложная бизнес-логика и если монга подходит в качестве хранилища, со всеми вытекающими критериями. Наверное, такими условиями отбрасываются 95% проектов, если не больше, и, конечно, есть множество других вопросов, которыми стоит задаться при выборе. Но в общем случае, я считаю, что метеор хорошо подходит для приложений, где важна коллаборация в реальном времени, например: форумы, хелпдески, системы управления задачами и т. д.
Возможно из-за удачного стечения обстоятельств, но я не натыкался на как таковые грабли, связанные с метеором. Была особенность: одно приложение надо было завязать на множество различных баз данных, но решение с полпинка было найдено, причем с сохранением всех плюшек метеора. Периодически мне встречается мнение, что метеору место на помойке, с чем никак не могу согласиться, и имея опыт работы с ним, в будущем с удовольствием выберу его снова, если будет такая возможность и если будет подходящий проект. Это все несколько общие слова, но если есть конкретные вопросы, готов ответить в личных сообщениях.
Но в общем случае, я считаю, что метеор хорошо подходит для приложений, где важна коллаборация в реальном времени, например: форумы, хелпдески, системы управления задачами и т. д.Разделяю это мнение, правда кругозор в JS-фреймворках у меня не особо большой.
Ваш коммент больше, чем моя статья. Спасибо за аргументы.
При переходе между страницами сначала появляется контент, а только чрез пол секунды — к нему подцепляются стили — всё очень неприятно скачет. И это на одностраничном сайте.
Добавление поста не работает, да и выглядит паршиво. Не удалось реализовать за 15 минут?
Я так и не понял, зачем вам тут потребовался реакт, чтобы нарисовать несколько статичных html-шаблонов, практически без динамики.
При переходе между страницами сначала появляется контент, а только чрез пол секунды — к нему подцепляются стили — всё очень неприятно скачет. И это на одностраничном сайте.
Подправил костылем, как заставить Helmet подключать стили правильно — может кто подскажет?
Добавление поста не работает
F12, после отправки формы нужно смотреть сюда:

Я говорил про добавление в Store.
А что толку от добавления в стор без обновления интерфейса?
А helmet правильно стили добавляет, но делает это динамически, во время рендеринга. Очевидно, для продакшена такой подход не годится.
А helmet правильно стили добавляет, но делает это динамически, во время рендеринга. Очевидно, для продакшена такой подход не годится.
пробовал бороться с ним вот так, локально помогало, а в продакшене опять беда:
componentWillMount() {
document.body.style.display = 'none'
}
componentDidMount() {
// HACK стили подключаются в Helmet после рендеринга страницы
setTimeout(
() => document.body.style.display = 'block'
, 100)
}
react-helmet, по-моему, слабо заточен на работу с ресурсами, требующими загрузки. Для них нужно маунтить основной компонент, когда ресурс уже загружен (чего может вообще никогда не произойти, например из-за 404). Он хорошо работает, когда нужно не внешний стиль/скрипт подключить, а прямо в тегах style/script писать css/js. А чем вам что-то вроде
import React from 'react';
import styles from './styles.css';
export default class HabrPage extends ReactComponent {...};
не нравится?
Правильная постановка вопроса — половина решения. Нужно отловить момент загрузки скриптов в Helmet!
А чем вам что-то вроде не нравится?
Всё дело в том, что для 404 на Хабре подключаются другие стили (смотри /components/NotFound.js). Т.е. нужно было как-то разблюдовать. Импорт же подключает стили в проект намертво. Есть мысли подгружать саму страницу динамически, используя require.
А как-то так:
import React, { Component } from 'react'
import Helmet from 'react-helmet'
import styles from './../../public/styles/access_deny.css'
class NotFound extends Component {
render() {
return (
<div id="layout">
<Helmet
defaultTitle="Хабрахабр"
/>
<div className={styles.main}>
<div className={styles.logo}>
<a href="/#/" title="На главную страницу"><img alt="" src="https://habrahabr.ru/images/logo.svg"/></a>
</div>
<h1>Страница не найдена</h1>
<p>Страница устарела, была удалена или не существовала вовсе</p>
<div className={styles.buttons}>
<a href="/#/" className={styles.button}>Вернуться на главную</a>
</div>
</div>
</div>
)
}
}
Вообще говоря, компонентный подход предполагает, что и js-код, и разметка, и стили хранятся если не в одном файле (есть любители CSS в JS непосредственно писать, но меня они в удобстве подхода не убедили), то очень близко друг от друга, как-то так:
src/components/NotFound/index.js
src/components/NotFound/styles.css
При переделке существующего приложения на реакт, я просто в каталог каждого компонента копировал все стили, импортировал их в JS, а уж потом в каждом отдельном компоненте вычищал лишнее.
Спасибо, до меня дошло! За лесом не видно деревьев. Так можно тупо переопределить стили для каждого HTML-тега в компоненте NotFound.
Так можно определить стили вообще для каждого компонента и забыть про них в других местах :)
Так и было сначала, использовал другую утилиту для извлечения компонентов. Но она не понимает русские буквы и для каждого компонента добавляет id, а если id были определены в первоначальной верстке, то их затирает. В морг.
Победил подключение стилей в Helmet после его рендеринга!
class NotFound extends React.Component {
constructor(props) {
super(props)
this.state = {
isRenderedHelmet: false,
}
}
render() {
return (
<div className="not-found">
<Helmet
title="MyApp - 404"
onChangeClientState={() => {
if (!this.state.isRenderedHelmet)
this.setState({
isRenderedHelmet: true,
})
}}
>
<style type="text/css">{`
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
overflow: hidden;
}
div#root {
padding: 0 56px;
display: flex;
min-height: 100%;
overflow: auto;
}
a {
text-decoration: none;
}
.not-found {
height: 50vh;
margin: auto;
}
.logo img {
width: 100px;
height: 100px;
}
h1 {
font-weight: normal;
}
.back-button {
border: 2px solid gray;
border-radius: 4px;
padding: 8px;
background: #eee;
display: inline-block;
}
.back-text {
color: black;
}
`}</style>
</Helmet>
{this.state.isRenderedHelmet &&
<div>
<div className="logo">
<Link to="/" title="Go to the main page"><img alt="logo" src={logo} /></Link>
</div>
<h1>Page not found</h1>
<p>The page is deprecated, deleted, or does not exist at all</p>
<div className="back-button">
<Link to="/"><span className="back-text">Go back to the main page</span></Link>
</div>
</div>}
</div>
)
}
}
Судя по комментариям в этом треде, напрашивается мысль "I had 99 problems. Then I used React. Now I have 101 problem."
В чем суть статьи? Показать, как худо-бедно сделать драфт? Или как использовать все вышеперечисленные библиотеки (react-helmet, redux-act, redux-thunk)? Но ведь ничего из этого нету в статье...
Пока не хватает примера только для redux-thunk. Остальное все есть в исходниках, в статье указано, куда смотреть.
Ну так почему бы не не уместить это все в статью?
Первые пять минут фильма "Быстрее пули" со Скалой — шедевр. Потом всё скатилось, как обычно.
Краткость — сестра сами знаете чего.
По-моему вы не поняли, что я пытаюсь донести. Высказывание "краткость — сестра таланта" хорошо работает для Твиттера. На блогах вроде Хабра или Медиума, как мне кажется, пользователи хотят видеть статьи, раскрывающие какую-либо тему. Ссылку на репозиторий гитхаба можно уместить и в 140 символов.
восстановил работоспособность плашки "#scroll_to_top"
Самое главное на сделано! xD
Именно, и в этом вся прелесть. Есть конечно SSR, благодаря которому сможешь работать, как в привычном PHP. Но в данном примере все выполняется на клиенте. Если собрать под Electron-ом, то получим кросс-платформенное десктоп-приложение. Если заменить React на ReactNative, то получим мобильное приложение. Благодаря Redux-у, определена архитектура круговорота данных в приложении. Дальше нужно подключиться к внешнему REST-API или Websocket-серверу. И можно купить backend, как услугу, засматриваюсь на scorocode.ru
StyleBot уже давно скрывает все мусорные блоки, объявления, кросс-ссылки.
Жаль родительский сайт для сохранения скриптов лежит, кто-нибудь знает достойные альтернативы StyleBot?
// в этом месте:
export const ga = (eventCategory, eventAction, eventLabel) => {
if (typeof window.ga === 'function') {
window.ga('send', 'event', eventCategory, eventAction, eventLabel)
}
}
// добавил замыкание:
export const ga = (eventCategory, eventAction, eventLabel) => () => {
if (typeof window.ga === 'function') {
window.ga('send', 'event', eventCategory, eventAction, eventLabel)
}
}
// теперь вместо похабного:
<a ... onClick={ga.bind(void 0, 'footer', 'links', 'ios_app')} >
// несравненная красота:
<a ... onClick={ga('footer', 'links', 'ios_app')} >
Мемоизация?
Переведи! :)
Внутренний кеш для уже возвращенных ранее сочетаний параметров. Есть готовые решения типа https://github.com/medikoo/memoizee
Только стоит учитывать, что без отслеживания зависимостей этот кеш будет храниться до скончания веков, так что не стоит мемоизировать ответы сервра.
вот она, сила функционально программирования на практике
заменил на lodash.memoize
отдельное спасибо за memoizee, lodash.memoize работает "иначе":
import memoizee from 'memoizee'
import memoize from 'lodash.memoize'
const test1 = memoizee((a, b, c) => (d) => {
console.log('test1', a, b, c, d)
})
const a1 = test1(1,2,3)
const b1 = test1(1,2,3)
const c1 = test1(1,2,4)
console.log('a1 === b1', a1 === b1) // a1 === b1 true
console.log('a1 === c1', a1 === c1) // a1 === c1 false
a1(1) // test1 1 2 3 1
b1(2) // test1 1 2 3 2
c1(3) // test1 1 2 4 3
const test2 = memoize((a, b, c) => (d) => {
console.log('test2', a, b, c, d)
})
const a2 = test2(1,2,3)
const b2 = test2(1,2,3)
const c2 = test2(1,2,4)
console.log('a2 === b2', a2 === b2) // a2 === b2 true
console.log('a2 === c2', a2 === c2) // a2 === c2 true - как-так-то?
a2(1) // test2 1 2 3 1
b2(2) // test2 1 2 3 2
c2(3) // test2 1 2 3 3 - как-так-то?
А будет ли оно работать в таком виде?
import memoize from 'lodash.memoize'
export const ga = (eventCategory, eventAction, eventLabel) => memoize(() => {
if (typeof window.ga === 'function') {
window.ga('send', 'event', eventCategory, eventAction, eventLabel)
}
})
Изменил структуру папок: вместо раздельных actions и reducers, объединил попарно файлы в папке ducks. Мы согласились на HTML внутри JS, зачем же бегать за экшенами и редюсерами по разным файлам?
ducks/editPost.js
import { createAction, createReducer } from 'redux-act'
export const actions = {
inputTitle: createAction('@@edit_post/INPUT_TITLE', title => ({ title })),
submit: createAction('@@edit_post/SUBMIT', post => post),
}
const initialState = {
flow: '',
title: '',
content: ''
}
const reducer = createReducer({
[actions.inputTitle]: (state, { title }) => ({...state, title}),
[actions.submit]: (state, post) => ({...state, ...post})
}, initialState)
export default reducer
На мой вкус — замечательно!
Косяк на git-pages, видимо придется переезжать на firebase.com, там тоже можно подцепить свой домен.
Изменил везде в коде < a > на < Link >, и вернул browserHistory вместо hashHistory. Теперь роутинг работает нормально без хешей.
Как слямзить Хабр по-быстрому