Pull to refresh

Comments 108

А tree shaking разве нормально отработаботает, если Вы импортируете всё подряд?
Здорово, спасибо
Работает даже такое

import { Math } from './exports.js';
console.log( Math['square']( 5 ) );
import { Math } from './exports.js';

const prop = 'square';
console.log( Math[prop]( 5 ) ); // 125

А такое — уже нет.


В общем, надурить Rollup можно, если постараться. Но основные варианты использования он поддерживает

Все хорошо, но конвенция именования файла @Exports очень странная.


Node.js и все бандлеры поддерживают index.js файл. Когда вы укажете в импорте путь до папки, например ../Common/Utils/, то бандлер автоматически добавить index.js или index.ts в конец, если такой файл в папке имеется.


И второй момент, вот такой код


import * as Common from "../Common/@CommonExports";

export { Common };

Ломает вам весь tree-shaking. Теперь все содержимое Common будет включено в бандл, неважно, используется ли оно на самом деле или нет

Хорошая мысль насчет index.ts, попробуем. Надо посмотреть, умеет ли SystemJS такое...

Обнаружил пару проблем с index.ts (собственно, почему я отказался от просто @Exports.ts и пишу @EventsExports):


  1. При редактировании вкладка в IDE называется index.ts — трудно понять по названию вкладки, что за файл
  2. При открытии через быстрый поиск нельзя найти именно этот файл, т.к. имя не уникальное (я использую FastFind)

Ну и возникает вопрос, как именовать imports и internals...

Во-первых, одинаковое имя лечится настройкой "показывать имя папки для одноименных файлов".
Во-вторых, в индексных файлах у вас содержатся только импорты/экспорты, часто в них лазить, а тем более держать несколько открытых сразу не придётся.
И в-третьих, ваш подход неэргономичен. После работы на проекте с общепринятым использованием index.js файлов приходишь в проект, где горе-архитектор не осилил настройку IDE, зато выдумал свою особую конвенцию именования, и эффективность работы только падает...

Все-таки пока не могу решить, как лучше, но в статью добавил.

Проверил еще раз, tree-shaking все еще работает. Пример в Rollup-repl. Второй вопрос снимается, извините.


А первый вопрос про неиспользование index.js все еще остается.

На что только не идут люди, чтобы не пользоваться нормальными средствами разработки. Я конечно сейчас накину на вентилятор, но ни одна религия не запрещает использовать WebStorm, в котором импорт, рефакторинг и навигация уже работает. Я вообще в импорты не смотрю, они зафолжены всегда.
А вебшторм умеет автоматически импортировать (как решарпер по Alt+Enter) из вариантов имеющихся в проекте? Например я начинаю печатать — а вебшторм зная что у меня имеются /path1/MyComponent и /path2/MyComponent / — предлагает мне заимпортить один из двух?
Да, это зарелизили месяц назад буквально.

Не знаю что конкретно нового они недавно зарелизили, но я еще в прошлом году мог выбирать по Alt Enter нужный символ, и добавлялся импорт именно выбранного символа.


А так да, импорты хоть 5, хоть 10 точек, неважно. Все автоматом делается средой. Не могу представить тот гемор если бы делал бы это сам (и тот гемор на который люди себя толкают выбирая другие инструменты разработки).

религия то конечно позволяет, а вот финансовое состояние, нет.

Пробовал VS Code, Atom, у них у всех ужасно работают TypeScript-плагины (по сравнению с VisualStudio). Может я пробовал на слишком слабом компьютере, не знаю.

Ужасно в смысле медленно? Они все используют TypeScript Language Service написанный на JS со всеми вытекающими. Большая студия я подозреваю использует свою шуструю реализацию.

Да, медленно обновляется кеш intellicense, медленно работает compile-on-save, глючит периодически (например, compile-on-save не замечает что были внесены правки в код, и не выполняет компиляцию). VisualStudio работает на порядок быстрее и стабильнее.

У разраба нет 150 баксов в год на инструмент для работы? Поработайте в выходной на фрилансе и купите себе инструмент. Напишите письмо в JB с просьбой дать скидку. Попросите лицензию на работе. *Not affiliated with JB*
я пока студент, так что юзаю все бесплатно

А еще может быть так, что в компании уже куплена VisualStudio, которая во всем всех устраивает, в ней настроены все дев-процессы (сборка, отладка и т.д.) и покупать еще отдельно другую IDE только из-за того что она умеет дописывать import и в результате получить гемор с перенастройкой процессов и переучиванием разработчиков — да никто не будет таким заниматься...

Есть мнение, что если import может автоматически вставлять IDE, то он с тем же успехом может автоматически вставляться и сборщиком.

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

Для я даже не про них, а про то, что сейчас развитие языка идёт в сторону увеличения писанины, которая могла бы быть легко автоматизирована.


Ведь можно же было посмотреть, как это сделано у других. Например, в пхп есть чудесный автолоад: http://www.php-fig.org/psr/psr-4/


Кроме того, про отказ от глобальной области видимости вы погорячились — тот же трендовый redux хранит все данные в одной глобальной области видимости. Из-за чего люди, привыкшие к импортам и возможности использовать короткие имена, огребают, когда надо соединить несколько приложений в одном: https://habrahabr.ru/company/efs/blog/328012/

Что autoloader в php, что агностик-модули, все заменяют import строгими правилами именования сущностей. То есть, конфликты имен в глобальной области видимости решаются конвенциональным путем. Проблема такого подхода очевидна — длинные имена и сложность заставить всех писать уникальные имена в правильном виде.


Про Redux… Там ведь не только конфликты имен могут быть, но совершенно неконтролируемое сцепление различных частей приложения, к тому же без поддержки соотв. тулинга. Мы (императивные программисты) инкапсулируем еще со времен модульного программирования, но функциональщики вообще не от мира сего ^_^


А про писанину, об этом как раз моя статья. Мне удалось уменьшить ее количество вплоть до одной-двух строчек import.

строгими правилами именования сущностей

И это очень хорошая практика. Порядком задалбывает разбираться как в очередном проекте решили раскидать модули по папкам. А если ещё и с алиасами, то вообще туши свет. На одном из проектов jQuery таким образом подключался аж 3 раза разных минорных версий.


длинные имена

Вы всегда можете сделать короткий локальный алиас, если имя слишком длинное.


сложность заставить всех писать уникальные имена в правильном виде

Ничего сложного. Неправильно написал — получил ошибку.


функциональщики вообще не от мира сего

Ну так, в ФП, изменяемое состояние является внешним по отношению к приложению, а значит общим для всего кода.

UFO just landed and posted this here

А я просто настроил paths и baseUrl в конфиге тайпскрипта, импорты выглядят так:


// PROJECT_ROOT/src/app/foo/foo.ts
export class Foo {}

// PROJECT_ROOT/src/app/fo/index.ts
export * from './foo.ts';

// PROJECT_ROOT/src/app/bar/bar.ts
import { Foo } from 'app/foo';

Вебшторм же можно настроить на импорт в таком стиле, без вездесущих ../../


картинка
Мы избавились от необходимости импортировать каждый используемый модуль по отдельности, создавая огромную import-шапку в каждом файле. Вместо этого один раз импортируем нужные пакеты из imports-файла своего пакета.

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

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

Это при условии наличия системы сборки (что по большому счёту верно на сегодняшний день). Но при нативных модулях и HTTP/2 гораздо лучше использовать явные импорты нужных вещей. Это делает зависимости более прозрачными и загрузку более точечной.


P.S. Пока нет нормальной работы с путями и перехватом путей в браузере, чтобы можно было делать алиасы, подсовывать заглушки в тестах и прочее, я пользуюсь RequireJS (и у меня нет полноценной системы сборки, LiveScript пофайлово компилируется с помощью File Watchers во время написания)

В typescipt можно делать так:

"baseUrl": "./",
"paths": {
    "pkg-*": ["./packages/pkg-*"]
}


В webpack:

resolve: {
   modules: [
      path.join(__dirname, '..', 'packages'),
      path.join(__dirname, '..', 'node_modules')
   ]
}


После этого просто разбиваешь проект на саб пакеты и используешь:

import { BasicEvent } from 'pkg-common/Utils/Events/BasicEvent';
import { Button } from 'pkg-components/Button';

Я не уверен, публиковалось ли уже где-то такое решение в данном контексте

У Angular в доках это называется barrel

Спасибо, добавил в статью.

Ваш велосипед суть половина моего. Ваши домены — отчасти мои пакеты, только "в одну сторону". Вы упорядочили импорт доменных компонентов (exports-файлы), но не упорядочили зависимости самих доменов (imports и internals — файлы). Lerna — абсолютные пути позволяют избавиться только от точек. Я же решаю массу других проблем.

Глобальная область видимости (aka namespace в TypeScript) — уже давно не круто.

Может и "не круто", зато удобно, надёжно и практично. Взять в пример ту же Java — там импорты идут по полному пути от корня (причём даже не корня проекта, а глобального корня), которые однозначно мапятся на директории. Это к вопросу о "../../..".


Далее, портянку импортов и rollup можно выкинуть в пользу агностик модулей. Собираться будет ровно то, что используется. Поддерживается любыми IDE и редакторами. Для вашего примера это будет так:


/My/Pages/LogIn/Form/Form.tsx


// Содержимое /My/Components/Field/Group/ и /My/Common/Utils/Events/Basic/ будет включено в бандл автоматически
namespace $My.Pages.LogIn {

    // Хотим - напрямую используем
    var event = new $My.Common.Utils.Events.Basic();

    // Хотим, "импортируем" в короткий алиас
    const FieldGroup = $My.Components.Field.Group

    export function Form() {
        return (
            <form>
                <FieldGroup name="blabla" />
                <FieldGroup name="lalala" />
            </form>
        )
    }

}

/My/Components/Field/Group/Group.tsx


namespace $My.Components.Field {

    export function Group() {
        return <fieldset />
    }

}

/My/Components/Field/Group/Group.css


// Стили тоже подтянутся автоматически.
fieldset {
    // ...
}

/My/Common/Utils/Events/Basic/Basic.ts


namespace $My.Common.Utils.Events {

    export class Basic {
        // ...
    }

}

Собираться может и будет, но вот SystemJS вы таким образом не настроите (чтобы грузить по одному файлу во время разработки). Мы давно используем namespace c формированием списка файлов и бандла через ASP.NET. Много боли несет в себе такая практика.


Собирать же бандл в дев-окружении, имхо, очень неудобно, т.к. приходится ждать сборки после каждой правки в коде. Я привык уже нажать Ctrl+S, и сразу видеть результат, без всяких source map (которые глючат и тормозят).

вот SystemJS вы таким образом не настроите

Да как-то не вижу в нём необходимости.


Много боли несет в себе такая практика.

Какой?


приходится ждать сборки после каждой правки в коде

Она быстро происходит, так как все файлы уже есть в памяти, надо только подгрузить изменившиеся, прогнать проверку типов (а её по отдельному файлу не прогнать, только по бандлу) и сконкатенировать (плёвая операция).


Я привык уже нажать Ctrl+S, и сразу видеть результат, без всяких source map (которые глючат и тормозят).

Эм… у вас браузер поддерживает TS? :-)

При сохранении срабатывает compile-on-save, который мгновенно компилит один единственный файл (ну а после докомпиливает остальное). Через SystemJS скомпиленный файл тут же подгружается при F5 в браузере. Никаких бандлов, все быстро и удобно.


Какой?

Боль там от управления зависимостями — приходится вручную прописывать, какой файл сначала, какой потом. Плюс при наследовании для TypeScript нужно указывать reference-тэг на файл с родительским классом.

При сохранении срабатывает compile-on-save

Не всякий раз после сохранения требуется видеть результат. Зато всякий раз, когда требуется видеть результат — требуется его наиболее актуальная версия.


мгновенно компилит один единственный файл

Для тайпчекинга всё-равно нужны все остальные файлы.


Через SystemJS скомпиленный файл тут же подгружается при F5 в браузере. Никаких бандлов, все быстро и удобно.

Всё быстро, пока число файлов и глубина зависимостей не большие.


Боль там от управления зависимостями — приходится вручную прописывать, какой файл сначала, какой потом. Плюс при наследовании для TypeScript нужно указывать reference-тэг на файл с родительским классом.

MAM разруливает это сам, не требуя от программиста лишних телодвижений — только следование соглашению об именовании. Присмотритесь к моему примеру выше.

Собственно, видео процесса, если интересно:


У нас пока не было необходимости шарить стейт между компонентами, там особая специфика предметной области. Но в целом, я вообще не понимаю, откуда столько хайпа вокруг Redux:
1) При малейшем изменении стейта запускаются все селекторы, обновляются все компоненты (WTF?). Отсюда все эти shouldComponentUpdate и т.д.
2) Никакой инкапсуляции — весь стейт доступен всем, причем сырые данные, без оберток.
3) TypeScript тулинг — сплошные проблемы...


Мне больше нравится подход Angular, со старыми добрыми сервисами, которые инкапсулируют внутри себя некоторую логику и стейт + предоставляют возможность подписки на изменения.

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

Хм, а whyrun не помогает?..

Я об этой проблеме прочитал в комментах к одной статье, сам не пробовал. Помогает или нет вот у Вас хотел бы спросить...


Вот в целом, какой смысл в MobX? Это все на случай если влом объявить событие и тригернуть его при смене свойств? То есть, просто синтаксический сахар, чтобы при объявлении свойства сразу объявлять и событие об его изменении? Если дело только в этом, то я за пол часа напишу свой декоратор, который это автоматизирует.


Посмотрел вот еще доки, оказывается @observable нельзя объявлять для свойств с сеттерами. То есть, я буду вынужден открывать в сервисе свои сырые данные, без возможности их инкапсулировать…

Нет, вам не надо открывать свои сырые данные. Декоратор @observable вообще-то можно и на приватные свойства повешать.


А на свойства с одним только геттером полагается либо вешать @computed — либо оставлять так если оно слишком простое.


Смысл MobX — в автоматическом определении зависимостей. MobX как раз решает ту самую проблему с постоянными проверками всего подряд.


Сам я еще MobX не пробовал, сижу пока на knockout, у которого та же идея — но не такая удобная реализация. Будет новый проект — попробую MobX.

Он слишком многословен.

KnockOut


class Foo {

    length = ko.observable( 2 )

    squared = ko.pureComputed({
        read : ()=> this.length() ** 2 ,
        write : ( next: number )=> {
            this.length( next ** .5 )
        } ,
    } )

}

MobX


class Foo {

    @observable
    length = 2

    @computed
    get squared() {
        return this.length ** 2
    }
    set squared( next : number ) {
        this.length = next ** .5
    }

}

$mol_mem


class Foo {

    @ $mol_mem()
    length( next = 2 ) { return next }

    @ $mol_mem()
    squared( next? : number ) {
        return this.length( next && next ** .5 ) ** 2
    }

}

Вы лучше покажите что дальше с этим делать.

Я показал, что многословность приблизительно одинаковая. Что с этой информацией делать — это уж вы решайте сами :-)

Вы показали что многословность приблизительно одинаковая в этом случае.

Ну, предложите другой случай, посмотрим.

Ваш $mol просто ужасен в части вывода.

Вывода чего? Вы можете хоть как-то аргументировать, а не просто бросаться оценочными суждениями?

Вывода информации. Генерирования html. Показа всей этой крутой реактивной модели пользователю.

$mol_mem ничего такого не умеет. Это чистая реализация ОРП, которую можно взять и использовать в любом проекте. Пару лет назад, я, например, использовал его с Ангуляром.


А вот $mol_view — это отдельная библиотека, использующая возможности ОРП по максимуму для построения интерфейса. Если максимум вам не нужен — можете использовать хоть Реакт, хоть Хэндлбарс.


Ну, и раз уж, речь зашла про $mol_view, то не поясните, что там такого "ужасного"? Только объективно, а не "непривычно и лень вникать".

А у вас есть готовые решения для подключения $mol_mem к React?

А там нужны какие-то решения?


Заворачиваем рендеринг реакта в атом:


const ui = new $mol_atom( 'render' , ()=> React.render( <UI/> , document.body ) )

ui.actualize()

Добавляем декоратор перед render, чтобы результат кешировался:


@ $mol_mem()
render() {
    ...
}

Всё, теперь можем обращаться к любым реактивным переменным.

Вы предлагаете рендерить каждый раз дерево целиком? Мда...

Ну так эти statefull components вызывают ReactDOM.render под капотом. А ссылки к чему? shouldComponentUpdate при использовании $mol_mem не нужен.

Суть в том, что при обновлении только одного компонента все дерево целиком рендериться не должно. А вы предлагаете рендерить.

Реакт так работает, что он на любой чих создаёт новое виртуальное дерево целиком. Потом смотрит разницу с предыдущим виртуальным деревом и применяет её к реальному дереву.

Если бы это было так — то все оптимизации рендеринга не давали бы никакого прироста.

Всё "оптимизации" Реакта сводятся к тому, чтобы render выдавал одно и то же значение, если то, от чего он реально зависит, не изменилось. Именно это и делает $mol_mem.

А как ваш $mol_mem учитывает изменения в props?

Никак, в пропсы надо передавать не сами значения, а функции получения/изменения значения. Тогда render вложенной компоненты подпишется на те атомы, от которых он реально зависит, а render владельца не подпишется. Также это позволит получать данные лениво по требованию. Кстати, независимость от props, позволяет не лепить костыли с сохранением обработчиков событий — их можно смело создавать при каждом рендеринге.


class Welcome extends React.Component {

  @ $mol_mem()
  render() {
    return <h1>Hello, { this.props.name() }</h1>;
  }

}

class App extends React.Component {

  @ $mol_mem()
  name( next = 'Anon' ) { return next }

  @ $mol_mem()
  render() {
    return <Welcome name={ ()=> this.name() } />
  }

}

Зато позволяет такие штуки делать:


class App extends React.Component {

  @ $mol_mem()
  profile() {
    return $mol_http_resource_json.item( '/profile.json' ).json()
  }

  name() {
    return this.profile().name
  }

  @ $mol_mem()
  render() {
    return <Welcome name={ ()=> this.name() } />
  }

}```

Я тут подумал… нет, горбатого могила исправит, реактовая реконциляция тут всё испортит. Поэтому остаётся писать так:


  @ $mol_mem()
  render() {
    return <Welcome name={ this.name() } />
  }

И прикрутить какой-нибудь костыль для пропсов.

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


Event-Driven архитектура очень хрупкая, потому что приходится вручную следить за своевременными подписками и отписками, а человек — существо ленивое и не сильно внимательное.

И все-таки дело в лени: ) Имхо, лучше уж хрупкость event-driven, чем хрупкость и закадровая магия-автоматика FRP…


Кстати, такой вопрос. На сколько я понял, основные проблемы связаны с автотрекингом зависимостей. Что мешает сделать объявление зависимостей явным? По-моему, это сделает систему куда элегантнее, стабильне и проще.

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

Ой да ладно, нет их, как же. Вышеописанный knockout немало крови у меня попил, когда я намешал deferred, trottle, и обычные computed в одном флаконе. Сказать что там всё расползлось, это всё равно, что ничего не сказать. Пришлось внутри computed лепить костыли, на случай если часть зависимостей, которые идут вначале тела-computed метода почему-то не обновились, а от них зависит логика вызова последующих зависимостей. И больше всего при этом убивает непредсказуемость такого поведения. Попытка отловить такой баг сродне попытке найти выход из лабиринта с завязанными глазами.

Это проблема knockout, а не автотрекинга. Как будто если бы не было автотрекинга — то все эти deferred, trottle, и обычные computed заработали бы как надо… Все эти defered и trottle зачастую используются просто чтобы остановить комбинаторный взрыв при распространении изменений, и по сути являются костылями.


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


Кроме того, в mobx явно разнесли зависимые значения и реакции и они никогда не вычисляются вперемешку.

Вот, вот, "комбинаторный взрыв", "значение могло устареть" и еще куча страшных слов в той статье и комментариях к ней. По-моему, задача слишком сложная, что бы ее решать вот так автоматически.

Задача достаточно сложная чтобы решать ее можно было только автоматически.

Задача не тривиальная, но вполне решаемая. Думаю мне удалось запилить наиболее эффективное автоматическое решение.

  1. Нет, не все. Как раз redux это и автоматизирует
  2. Это скорее ООП головного мозга. Нет, я не в обиду — я сам люблю и практикую C#. Но тут другой подход и он хорошо работает.
    А если смотреть ещё глубже, то тут наоборот хорошее разделение — аналоги message bus & event sourcing в мире бэкенда.
  3. Тут да, есть гемор
Нет, не все. Как раз redux это и автоматизирует

Поправьте, если я где-то ошибусь. Согласно вот этим докам, у нас есть container-компоненты, которые реализуют функцию mapStateToProps. Эта функция, очевидно, получает на вход state и выдает props. Она вызывается при каждом изменении стейта в сторе. Соответственно, если функция выдает те же самые props, то компонент не перерисовывается (что есть обычная логика react).


Теперь, вопрос: чем все вот это отличается от тех самых кошмарных watch, в AngularJS? Есть некоторое глобальное состояние, и есть набор вотчеров, которые его смотрят. Единственное отличие — в AngularJS вотчеры могли еще сами менять state, здесь все стабилизируется за один проход. Но суть ведь та же — все компоненты проверяют стейт при каждом чихе…

А в чем проблема типизирования actions?

export interface Action<T extends string> {
	readonly type: T;
}

export interface PayloadAction<U extends string, T> extends Action<U> {
	readonly payload: T;
}

//

export const SET_USER = 'SET_USER';

export interface SetUserAction extends PayloadAction<typeof SET_USER, IUser> {}

export const setUser = (user: IUser): SetUserAction => ({
	type: SET_USER,
	payload: user
});

Ваш ребус мало кто сможет понять.

А можно подробнее? Что тут не понятного?

Не понятно зачем такие пляски с бубном, чтобы понять которые нужно 5 минут вникать в код.

Подождите, а вы писали что нибудь на typescript?

Я целый фреймворк на нём запилил, а что?

Если вы про $mol, то тогда понятно. Типизации там очень мало, как по мне, для typescript проекта.

Ну а «мало кто сможет понять» слишком преувеличено.

Где по вашему её не хватает?


Мало кто вообще TS понимает, а шаблонный код — тем более.

Зачем тут 5 минут в код вникать? Сразу же видно, что SetUserAction — это объект из двух полей, type и payload, причем первое строго равно 'SET_USER', а вторая имеет тип IUser.


Это даже IDE подсказать может.

Чтобы понять что имел ввиду автор и что помешало ему написать просто:


export function setUser( user : IUser ) {
    return {
        type : 'SET_USER' ,
        payload : user ,
    }
}
Во первых тогда type будет иметь тайп string, а не 'SET_USER';
Во вторых константа SET_USER нужна так же и в редьюсе, или там тоже строкой писать?
  1. 'SET_USER' as 'SET_USER'


  2. А почему бы и нет?
1. Ага и все сразу становится понятнее :)
2. Потому что это магия, да и опечатки сложнее искать:
2.1. + Интерфейс дает возможность валидировать экшн в редьюсеры и все типы отлично подсказываются в IDE.
export interface UserState {
	data: IUser;
}

const initialState: UserState = {
	data: null
};

export default createReducer({
	[SET_USER]: (state, { payload }: SetUserAction) => {
		return assign(state, {
			data: payload
		});
	}
}, initialState);

В этот ребус мне уже лень внимать, извините.

Ваш вариант хорошо выглядит.
+Возможно вместо interface вам удобнее type было бы использовать


export interface SetUserAction extends PayloadAction<typeof SET_USER, IUser> {}
// =>
export type SetUserAction = PayloadAction<typeof SET_USER, IUser>

В том, что дублирования много — надо объявить тип, надо указать тип в actionCreator'e, а при использовании redux-thunk в dispatch вообще нетипизированный экшен отправить легко. В reducers вообще тяжко типизировать.


Сейчас сделал так — actions это объект класса (назвал их messages). Поле type у message это имя класса.
Затем в редьюсере с помощью декоратора handler выдираю типы message и нахожу обработчик


// messages aka actions
export class Message {
    type = this.constructor.name;
}

export class FetchOffenseSegments extends Message {
    constructor(public payload: NIBRSOffenseSegment[]) {
        super();
    }
}

// action creators
export const fetchSegments = () =>
    api.getOffenseSegments()
        .then(segments => new FetchOffenseSegments(segments));

// reducers
class SegmentHandlers implements MessageReducer<NIBRSOffenseSegment[]> {
    state: NIBRSOffenseSegment[] = [];

    @handler handleFetchOffenseSegments(message: FetchOffenseSegments) {
        return message.payload;
    }

    @handler handleRemoveOffenseSegment(message: RemoveOffenseSegment) {
        return this.state.filter(x => x.id !== message.payload);
    }

    @handler handleImportCaseIncidents(message: ImportCaseIncidents) {
        return [...this.state, ...message.payload];
    }
}

export default combineReducers({
    segments: asReducer(SegmentHandlers),
    caseIncidents: asReducer(CaseIncidentHandlers)
});

Вы ведь знаете, что constructor.name не поддерживается в IE и изменяется при минификации?

и изменяется при минификации?

это отключаемая опция

constructor.name не поддерживается в IE

Для него есть костыль:


Function.prototype.toString.call( func ).match( /^function ([a-z0-9_$]*)/ )[ 1 ]

Полученное таким образом имя, разумеется, нужно закешировать. Лучше всего в WeakMap.

Как уже написали — uglify настроили, ie заполифили

Sign up to leave a comment.