Pull to refresh

Comments 66

Ох, сколько же бывалых веб-разработчиков оттолкнула каша из HTML-тегов внутри JavaScript-кода. У меня аллергия прошла через три года, а тут ещё ES6 подоспел — кошмар ретрограда. Но всё к лучшему, за это время экосистема React-а выросла и окрепла. Как раньше люди жили без Redux-а, наверно мучились. :)

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

Беда в том, что нет единственно правильного ответа. Пока React победил в моих изысканиях. Предыдущая ставка на Meteor — потерянное время.

С нетерпением ждём следующей серии в изысканиях :-)

Вообще-то я очарован Clojure. Но такую корову не продать.

Можете пару слов о Метеоре, пожалуйста? Начинаю на нём сейчас серьезный проект, интересны чужие грабли.

Слишком интимный вопрос, многих ранила моя статья эту тему. Отвечу в личку.

Спасибо, очень полезно. Как-то упустил статью.

Ещё хочу дать самые лестные рекомендации проекту 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 символов.

Все дело в том, что суть статьи осталась за горизонтом. :)

Добавление поста не работает, да и выглядит паршиво. Не удалось реализовать за 15 минут?

На это ушло три недели, и ещё есть над чем работать. Тынц.

восстановил работоспособность плашки "#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')} >

Но! Этот способ плох для передачи props в PureComponent, onClick={ga(...)} при каждом рендеринге будет возвращать новую функцию. Для понимания вопроса — видео.

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

вот она, сила функционально программирования на практике

отдельное спасибо за 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)
  }
})

неправильно! оборачивать нужно снаружи и применять memoizee (почему — смотри коммент выше)

Изменил структуру папок: вместо раздельных 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, расскажите. То github pages знаю (он же gh pages), а что такое git pages — не ведаю.

Sign up to leave a comment.

Articles