Как стать автором
Обновить
148
0
Зубашев Степан @faiwer

frontend-программист

Отправить сообщение
Но работает это только с отключенным sourceMap.

Ах вот почему оно у меня не работало. Спасибо за наводку. Поиграю с этим.

Я года четрые назад пытался пользоваться npm link. Спустя некоторое время свыкся с тем, что вся экосистема меня люто ненавидит. Я был кем-то вроде alpha-тестера. У меня ломалось вот буквально всё, что только могло сломаться. Оказалось что почти никто не разрабатывает свой код с учётом поддержки sym-линков. Отваливались тест-runner-ы, сборщики, backend библиотеки, docker (кажется), линтеры,… По сути сложно вспомнить что продолжало работать.


В какой-то момент я понял, что я 2 трети времени работаю над проектов, одну треть — чиню поддержку sym-линков. Даже сам npm link ломался при любой операции с npm.


Возможно сейчас всё уже не так плохо, но вышеописанный опыт отбил всякое желание прикасаться к sym-линкам со стороны JS окружений.

  1. Ну тут как сказать. Если это binary, то оно пишет "this request has no response data available". Если это что-то строковое, то умеет. Но всё равно — заголовки и тело ответа отдельно. Можно скопировать "response". Для blob оно копирует data-uri, для условного JSON-а body-часть ответа. Заголовки показывает в табличном виде. После ряда фильтров (когда дебажил это всё дело Wireshark-ом видел что реальные заголовки HTTP2 несколько отличаются. В общем если нужен простой ответ, то нет.
  2. Не видел такой возможности. Не уверен что такое есть даже в Roadmap
как по мне альтернативно грамотный человек — ну такое себе существо

Рекомендую к просмотру эту серию роликов — Речевой нюанс. Добавит вам лингвистической грамотности. Да и желание кого-либо поправлять искоренит. Должно придти понимание того, насколько язык является живым. То что казалось незыблемым вчера — становится правилом завтра. Про то как народ постоянно перетряхивает словарь (слово "тятя" вам очень в тему привели). Про прескриптивизм в худшем его проявлении (то чем вы занимаетесь).


После этого будете смотреть на людей, которые доблестно воюют за "он — кофе" как на доблестных воинов добрабобра со зломослом.

Можно то можно, но единственное преимущество моего решения выше (простота и прямолинейность) полностью исчезло. А асимптотика у всех 3-х решений одна и та же :)

По второй задаче можно немного проще (для восприятия), и без hashmap:


  1. Пробегаем по всей строке и заполняем массив, где arr[i] это количество уникальных символов от края до позиции i (для этого храним set задействованных символов)
  2. Ещё раз тоже самое, но с конца (т.е. один массив для "слева", другой для "справа")
  3. Пробегаем третий раз и сравниванием позиции arr[i] == arr[length - i - 2]. Если true, то +1.

Коду немного больше, но, имхо, он сильно проще для понимания из-за прямолинейности.


code
const calcUniq = (source: string): number[] => {
  const usedChars = new Set<string>();
  let count = 0;

  return [...source].map(char => {
    if (!usedChars.has(char)) {
      usedChars.add(char);
      count = count + 1;
    }

    return count;
  });
};

function numSplits(source: string): number {
  const left = calcUniq(source);
  const right = calcUniq([...source].reverse().join(''));

  let count = 0;
  for (let idx = 0; idx < source.length - 1; ++ idx) {
    if (left[idx] !== right[source.length - idx - 2])
      continue;
    ++ count;
  }
  return count;
};
test
ccoyyo
["wichbx","oahwep","tpulot","eqznzs","vvmplb","eywinm","dqefpt","kmjmxr","ihkovg","trbzyb","xqulhc","bcsbfw","rwzslk","abpjhw","mpubps","viyzbc","kodlta","ckfzjh","phuepp","rokoro","nxcwmo","awvqlr","uooeon","hhfuzz","sajxgr","oxgaix","fnugyu","lkxwru","mhtrvb","xxonmg","tqxlbr","euxtzg","tjwvad","uslult","rtjosi","hsygda","vyuica","mbnagm","uinqur","pikenp","szgupv","qpxmsw","vunxdn","jahhfn","kmbeok","biywow","yvgwho","hwzodo","loffxk","xavzqd","vwzpfe","uairjw","itufkt","kaklud","jjinfa","kqbttl","zocgux","ucwjig","meesxb","uysfyc","kdfvtw","vizxrv","rpbdjh","wynohw","lhqxvx","kaadty","dxxwut","vjtskm","yrdswc","byzjxm","jeomdc","saevda","himevi","ydltnu","wrrpoc","khuopg","ooxarg","vcvfry","thaawc","bssybb","ccoyyo","ajcwbj","arwfnl","nafmtm","xoaumd","vbejda","kaefne","swcrkh","reeyhj","vmcwaf","chxitv","qkwjna","vklpkp","xfnayl","ktgmfn","xrmzzm","fgtuki","zcffuv","srxuus","pydgmq"]

Сразу уточню, я не запускал его на коде из статьи. Запускал на своём. Общий принцип тот же самый. Моя failed версия вывела:


output
WORD 1 wichbx 0
WORD 2 oahwep 0
WORD 3 tpulot 0
WORD 4 eqznzs 0
WORD 5 vvmplb 0
WORD 6 kmjmxr 0
WORD 7 ihkovg 0
WORD 8 bcsbfw 1
WORD 9 abpjhw 0
WORD 10 uysfyc 1
WORD 11 ccoyyo 6
FOUND

Я поймал такой случай в одном из тестов на leetcode. 11 попыток, вместо 10. "Оптимальный" вариант сработал — 8 шагов вместо 11.

Что, если в начале взять некоторое рандомное слово str1 из списка, сравнить его с секретным, получить некоторое значение matchDistance(количество совпавших элементов), и после этого удалить из списка все те слова, у которых matchDistance со сторокой str1 отличается, так как если у этих слов это значение отличается, они не равны str1, значит не равны и секретному слову.

Я так и сделал. Failed. Благо leetcode даёт данные теста, на котором падает. Прогнал локально… эх, 11 попыток. Ровно на 1 больше лимита. Т.е. всё таки нужна стратегия выбора. Полагаю ваш код просто случайно прошёл тесты. Ну или мой сорвал "джекпот" вероятности.


Сильно всё усложнил. Теперь следующее слово выбирается по принципу: взять такое слово, чтобы в случае если guess вернёт точно такой же ответ, мы отрезали как можно больше слов. Для этого сделал квадратную матрицу пересечений всех со всеми, с которой потом сверялся. Success. Тот тест на котором всё упало теперь справляется за 8 попыток.


P.S. по русскому описанию 1-й задачи в статье ничего не понял. Залез на литкод — всё понятно. Ещё и с примерами.

Кажется в свежей версии React убрали (или вот вот уберут) этот warning, из-за бесконечных ложно-позитивных результатов.

update: я кажется что-то напутал. В @latest версии (17.02) warning всё ещё на месте. А того issue найти не удалось. Возможно я что-то напутал. Но выглядит всё так, словно все эти isUnmount и AbortController штуки по любому поводу всё ещё higly recommended. На мой взгляд (может быть наши особенности проекта просто такие), это false positive почти всегда.

const runAsyncFunctions = async () => {
    try {
        const employees = await fetchAllEmployees(baseApi)
        Promise.all(
            employees.map(async user => {
                const userName = await fetchEmployee(userApi, user.id)
                const emails = generateEmail(userName.name)
                return emails
            })
        )
    } catch (error) {
        console.log(error)
    }
}

вы тут await перед Promise.all забыли. Без него всё насмарку.


upd1. и ещё забыли return, иначе какой смысл писать return emails.
upd2. и скорее всего нужен .flat() а то у вас странный список email[][] получается :-) переменная названа криво, т.к. email один, должно быть email (без s).

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


Да я вижу, что смена items всё равно вызывает ререндер, но на такие вещи полагаться нельзя. Ни в коем случае. Минимальный рефакторинг в будущем, когда человек не будет иметь всей картинки костылей в голове, легко поломает этот "код". И да, текущее "случайно" работающее поведение это как раз костыль. Красный флаг.


Результатом стал многократный повторный рендеринг

Вот чтобы таких вещей не было надо вникать в то как хуки работают, какие задачи они выполняют, и каков вообще hook way в реакте. Судя по всему (по твоим ответам и коду в статье) ты пока пишешь "на ощупь". Отсюда и типовые ошибки и типовые костыли. Серьёзно, я не хочу обидеть, просто это видно издалека.


hasPrev и hasNext также были реактивными

Вот это тоже грубая ошибка. Которую, насколько я понял, ты уже усвоил. Тут действует простое правило — всё что можно посчитать на основе уже существующих данных — не нужно хранить в стейте. Максимум мемоизировать (useMemo), если вычисления тяжёлые. Причина банальна — ручная синхронизация = новый источник багов = дорого. В твоём случае ещё и rerender-ы.


многократный повторный рендеринг

Нет смысла, оптимизация будет преждевременной

За что боролся на то и напоролся. Когда пишешь core-вещи, т.е. обобщённый многократно переиспользуемый код (а твой хук как раз из таких), то это должна быть вылизанная до мелочей оптимизированная штука. Иначе — руки прочь из core части. Даже вопроса такого не должно возникать.


Я бы не сказал, что "хранить все в одном объекте" всегда проще

А где я сказал "всегда"? У нас на 80к строк кода всего несколько useReducer. У тебя как раз такой случай, когда useReducer упрощает понимание кода, убирает лишние рендеры, легко scale-уется в случае сложных доработок. То, что доктор прописал. Да ещё и все переменные тесно связанные между собой. ​Особенно если учесть что в настоящем боевом коде этот хук будет куда сложнее, когда полезут corner case-ы.


  1. Возможно.

Не возможно, а точно, я тебе говорю. На этапе system design такой ответ это красный флаг и "мы вам перезвоним". Тебе завтра потребуется подключить этот хук в другую часть приложения где более сложная работа с URL (или просто другая) и тебе придётся выпиливать всё до последней буквы. Да даже просто наличие на странице сразу двух постраничных виджетов (или списка списокв) и "приехали".


А причина банальная — это не задача для хука который занимается вызовом асинхр. метода который подтягивает данные согласно постраничной навигации. "low in coupling and high in cohesion" — вот главная мантра любой архитектуры. Из неё автоматически вытекает что не должно быть таких пучков которые умеют во всё сразу, особенно как-то конкретно (?page= в любой URL игнорируя рутинг приложения).

Разве у Promise именно "своя" очередь? Мануалы вроде говорят что они используют очередь микротасок.

Не совсем своя. Я просто не стал вдаваться в детали. Количество и состав очередей зависит от платформы. На мой взгляд эти нюансы мало кому важны, главное просто знать, что эта очередь "микротасок" есть, и с чем её едят. Чтобы, например, не уйти в вечный loop.


А в вашем примере макротаска заканчивается выделением микротаски и микротаска сразу стартует, давая условный 0.

Всё правильно.

Не очень понял почему там должен быть не 0

Суть в том что у promise-ов своя приоритетная (над обычной) асинхронная event-loop очередь. Поэтому там 0. Разгадка в этом. В то время как у setTimeout(,0) и даже у requestAnimationFrame это не так. По сути аналог, ЕМНИП, есть в nodejsprocess.nextTick.


ЕМНИП, то с её помощью можно даже повешать браузер\nodejs-процесс.

А лучше под <spoiler/>, чтобы не только главная не прыгала, но ещё и сама страница статьи.

Всё так. Но давайте мыслить дискретно позитивно! :-)

а зачем вообще появился throttle?

Нуууу… Скролл бывает очень дробным. Скажем проскроллил тачпадом на 15px, браузер вызвал 15 событий. Из которых 10 попало в один кадр. 10 раз был изменён textContent, а он, к примеру 3 раза поменял layout страницы (не моноширинный шрифт). Где-то плачет Грета и считает углеродный след. Но в целом согласен.

C requestAnimationFrame результат может быть даже лучше.


А если сделать Promise.resolve().then(fn) то вообще ноль покажет (это кстати довольно хардкорный JS вопрос на собесах). Но это уже немного не в тему, да :)

Когда вместо SELECT ... OFFSET {(page - 1) * limit} LIMIT {limit} используются более сложные схемы. Например берётся items.last().createdBy.toUnixTime() и возвращается в качестве курсора\якоря\как-угодно-можно-назвать. А на сервере WHERE createdBy >= {cursor}.


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

Не соглашусь. Можно смело вызывать setNumber на уже мёртвом компоненте. Ничего не случится. Кажется в свежей версии React убрали (или вот вот уберут) этот warning, из-за бесконечных ложно-позитивных результатов.


Но вот если там происходит, например, отправление формы и по успеху делается роутинг типа такого

ИМХО, только в таком случае и стоит так поступать. А более простой код калечить не стоит. YAGNI.


P.S. мы написали простенький useIsUnmounted: () => (): boolean для таких целей. Компактнее и нагляднее получается. Плюс можно переиспользовать для нескольких callback-ов и effect-ов.


const isUnmountFn = useIsUnmounted();

useEffect(() => {
  whatever().then(() => {
    if (isUnmountFn()) return;
    // do staff
  });
}, [/**/);

Но избегать обыкновенный useState этим — из пушки по воробьям. Лаконичность и простота кодовой базы сильно важнее.

Информация

В рейтинге
5 382-й
Откуда
Алматы (Алма-Ата), Алма-Атинская обл., Казахстан
Дата рождения
Зарегистрирован
Активность