Я года четрые назад пытался пользоваться npm link. Спустя некоторое время свыкся с тем, что вся экосистема меня люто ненавидит. Я был кем-то вроде alpha-тестера. У меня ломалось вот буквально всё, что только могло сломаться. Оказалось что почти никто не разрабатывает свой код с учётом поддержки sym-линков. Отваливались тест-runner-ы, сборщики, backend библиотеки, docker (кажется), линтеры,… По сути сложно вспомнить что продолжало работать.
В какой-то момент я понял, что я 2 трети времени работаю над проектов, одну треть — чиню поддержку sym-линков. Даже сам npm link ломался при любой операции с npm.
Возможно сейчас всё уже не так плохо, но вышеописанный опыт отбил всякое желание прикасаться к sym-линкам со стороны JS окружений.
Ну тут как сказать. Если это binary, то оно пишет "this request has no response data available". Если это что-то строковое, то умеет. Но всё равно — заголовки и тело ответа отдельно. Можно скопировать "response". Для blob оно копирует data-uri, для условного JSON-а body-часть ответа. Заголовки показывает в табличном виде. После ряда фильтров (когда дебажил это всё дело Wireshark-ом видел что реальные заголовки HTTP2 несколько отличаются. В общем если нужен простой ответ, то нет.
Не видел такой возможности. Не уверен что такое есть даже в Roadmap
как по мне альтернативно грамотный человек — ну такое себе существо
Рекомендую к просмотру эту серию роликов — Речевой нюанс. Добавит вам лингвистической грамотности. Да и желание кого-либо поправлять искоренит. Должно придти понимание того, насколько язык является живым. То что казалось незыблемым вчера — становится правилом завтра. Про то как народ постоянно перетряхивает словарь (слово "тятя" вам очень в тему привели). Про прескриптивизм в худшем его проявлении (то чем вы занимаетесь).
После этого будете смотреть на людей, которые доблестно воюют за "он — кофе" как на доблестных воинов добрабобра со зломослом.
Можно то можно, но единственное преимущество моего решения выше (простота и прямолинейность) полностью исчезло. А асимптотика у всех 3-х решений одна и та же :)
По второй задаче можно немного проще (для восприятия), и без hashmap:
Пробегаем по всей строке и заполняем массив, где arr[i] это количество уникальных символов от края до позиции i (для этого храним set задействованных символов)
Ещё раз тоже самое, но с конца (т.е. один массив для "слева", другой для "справа")
Пробегаем третий раз и сравниванием позиции 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;
};
Сразу уточню, я не запускал его на коде из статьи. Запускал на своём. Общий принцип тот же самый. Моя 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
Что, если в начале взять некоторое рандомное слово 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 почти всегда.
вы тут 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-ы.
Возможно.
Не возможно, а точно, я тебе говорю. На этапе system design такой ответ это красный флаг и "мы вам перезвоним". Тебе завтра потребуется подключить этот хук в другую часть приложения где более сложная работа с URL (или просто другая) и тебе придётся выпиливать всё до последней буквы. Да даже просто наличие на странице сразу двух постраничных виджетов (или списка списокв) и "приехали".
А причина банальная — это не задача для хука который занимается вызовом асинхр. метода который подтягивает данные согласно постраничной навигации. "low in coupling and high in cohesion" — вот главная мантра любой архитектуры. Из неё автоматически вытекает что не должно быть таких пучков которые умеют во всё сразу, особенно как-то конкретно (?page= в любой URL игнорируя рутинг приложения).
Разве у Promise именно "своя" очередь? Мануалы вроде говорят что они используют очередь микротасок.
Не совсем своя. Я просто не стал вдаваться в детали. Количество и состав очередей зависит от платформы. На мой взгляд эти нюансы мало кому важны, главное просто знать, что эта очередь "микротасок" есть, и с чем её едят. Чтобы, например, не уйти в вечный loop.
А в вашем примере макротаска заканчивается выделением микротаски и микротаска сразу стартует, давая условный 0.
Суть в том что у promise-ов своя приоритетная (над обычной) асинхронная event-loop очередь. Поэтому там 0. Разгадка в этом. В то время как у setTimeout(,0) и даже у requestAnimationFrame это не так. По сути аналог, ЕМНИП, есть в nodejs — process.nextTick.
ЕМНИП, то с её помощью можно даже повешать браузер\nodejs-процесс.
Нуууу… Скролл бывает очень дробным. Скажем проскроллил тачпадом на 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 этим — из пушки по воробьям. Лаконичность и простота кодовой базы сильно важнее.
Ах вот почему оно у меня не работало. Спасибо за наводку. Поиграю с этим.
Я года четрые назад пытался пользоваться npm link. Спустя некоторое время свыкся с тем, что вся экосистема меня люто ненавидит. Я был кем-то вроде alpha-тестера. У меня ломалось вот буквально всё, что только могло сломаться. Оказалось что почти никто не разрабатывает свой код с учётом поддержки sym-линков. Отваливались тест-runner-ы, сборщики, backend библиотеки, docker (кажется), линтеры,… По сути сложно вспомнить что продолжало работать.
В какой-то момент я понял, что я 2 трети времени работаю над проектов, одну треть — чиню поддержку sym-линков. Даже сам npm link ломался при любой операции с npm.
Возможно сейчас всё уже не так плохо, но вышеописанный опыт отбил всякое желание прикасаться к sym-линкам со стороны JS окружений.
Рекомендую к просмотру эту серию роликов — Речевой нюанс. Добавит вам лингвистической грамотности. Да и желание кого-либо поправлять искоренит. Должно придти понимание того, насколько язык является живым. То что казалось незыблемым вчера — становится правилом завтра. Про то как народ постоянно перетряхивает словарь (слово "тятя" вам очень в тему привели). Про прескриптивизм в худшем его проявлении (то чем вы занимаетесь).
После этого будете смотреть на людей, которые доблестно воюют за "он — кофе" как на доблестных воинов
добрабобра созломослом.Можно то можно, но единственное преимущество моего решения выше (простота и прямолинейность) полностью исчезло. А асимптотика у всех 3-х решений одна и та же :)
По второй задаче можно немного проще (для восприятия), и без hashmap:
arr[i]
это количество уникальных символов от края до позицииi
(для этого хранимset
задействованных символов)arr[i] == arr[length - i - 2]
. Еслиtrue
, то+1
.Коду немного больше, но, имхо, он сильно проще для понимания из-за прямолинейности.
Сразу уточню, я не запускал его на коде из статьи. Запускал на своём. Общий принцип тот же самый. Моя failed версия вывела:
Я поймал такой случай в одном из тестов на leetcode. 11 попыток, вместо 10. "Оптимальный" вариант сработал — 8 шагов вместо 11.
Я так и сделал. Failed. Благо leetcode даёт данные теста, на котором падает. Прогнал локально… эх, 11 попыток. Ровно на 1 больше лимита. Т.е. всё таки нужна стратегия выбора. Полагаю ваш код просто случайно прошёл тесты. Ну или мой сорвал "джекпот" вероятности.
Сильно всё усложнил. Теперь следующее слово выбирается по принципу: взять такое слово, чтобы в случае если guess вернёт точно такой же ответ, мы отрезали как можно больше слов. Для этого сделал квадратную матрицу пересечений всех со всеми, с которой потом сверялся. Success. Тот тест на котором всё упало теперь справляется за 8 попыток.
P.S. по русскому описанию 1-й задачи в статье ничего не понял. Залез на литкод — всё понятно. Ещё и с примерами.
update: я кажется что-то напутал. В
@latest
версии (17.02) warning всё ещё на месте. А того issue найти не удалось. Возможно я что-то напутал. Но выглядит всё так, словно все этиisUnmount
иAbortController
штуки по любому поводу всё ещё higly recommended. На мой взгляд (может быть наши особенности проекта просто такие), это false positive почти всегда.вы тут
await
передPromise.all
забыли. Без него всё насмарку.upd1. и ещё забыли
return
, иначе какой смысл писатьreturn emails
.upd2.
и скорее всего нуженпеременная названа криво, т.к. email один, должно быть.flat()
а то у вас странный списокemail[][]
получается :-)email
(безs
).Перечитай внимательно комментарий выше. Использование
useRef
для реактивных значений — грубая ошибка. Ибо это бомба замедленного действия. Тот самый скользкий тип багов, которые потом тяжело воспроизводить и выяснять причины странного поведения. Один из самых дорогостоящих видов багов для бизнеса.Да я вижу, что смена
items
всё равно вызывает ререндер, но на такие вещи полагаться нельзя. Ни в коем случае. Минимальный рефакторинг в будущем, когда человек не будет иметь всей картинки костылей в голове, легко поломает этот "код". И да, текущее "случайно" работающее поведение это как раз костыль. Красный флаг.Вот чтобы таких вещей не было надо вникать в то как хуки работают, какие задачи они выполняют, и каков вообще hook way в реакте. Судя по всему (по твоим ответам и коду в статье) ты пока пишешь "на ощупь". Отсюда и типовые ошибки и типовые костыли. Серьёзно, я не хочу обидеть, просто это видно издалека.
Вот это тоже грубая ошибка. Которую, насколько я понял, ты уже усвоил. Тут действует простое правило — всё что можно посчитать на основе уже существующих данных — не нужно хранить в стейте. Максимум мемоизировать (
useMemo
), если вычисления тяжёлые. Причина банальна — ручная синхронизация = новый источник багов = дорого. В твоём случае ещё и rerender-ы.За что боролся на то и напоролся. Когда пишешь core-вещи, т.е. обобщённый многократно переиспользуемый код (а твой хук как раз из таких), то это должна быть вылизанная до мелочей оптимизированная штука. Иначе — руки прочь из core части. Даже вопроса такого не должно возникать.
А где я сказал "всегда"? У нас на 80к строк кода всего несколько
useReducer
. У тебя как раз такой случай, когдаuseReducer
упрощает понимание кода, убирает лишние рендеры, легко scale-уется в случае сложных доработок. То, что доктор прописал. Да ещё и все переменные тесно связанные между собой. Особенно если учесть что в настоящем боевом коде этот хук будет куда сложнее, когда полезут corner case-ы.Не возможно, а точно, я тебе говорю. На этапе system design такой ответ это красный флаг и "мы вам перезвоним". Тебе завтра потребуется подключить этот хук в другую часть приложения где более сложная работа с URL (или просто другая) и тебе придётся выпиливать всё до последней буквы. Да даже просто наличие на странице сразу двух постраничных виджетов (или списка списокв) и "приехали".
А причина банальная — это не задача для хука который занимается вызовом асинхр. метода который подтягивает данные согласно постраничной навигации. "low in coupling and high in cohesion" — вот главная мантра любой архитектуры. Из неё автоматически вытекает что не должно быть таких пучков которые умеют во всё сразу, особенно как-то конкретно (
?page=
в любой URL игнорируя рутинг приложения).Не совсем своя. Я просто не стал вдаваться в детали. Количество и состав очередей зависит от платформы. На мой взгляд эти нюансы мало кому важны, главное просто знать, что эта очередь "микротасок" есть, и с чем её едят. Чтобы, например, не уйти в вечный loop.
Всё правильно.
Суть в том что у promise-ов своя приоритетная (над обычной) асинхронная event-loop очередь. Поэтому там 0. Разгадка в этом. В то время как у
setTimeout(,0)
и даже уrequestAnimationFrame
это не так. По сути аналог, ЕМНИП, есть вnodejs
—process.nextTick
.ЕМНИП, то с её помощью можно даже повешать браузер\nodejs-процесс.
А лучше под <spoiler/>, чтобы не только главная не прыгала, но ещё и сама страница статьи.
Всё так. Но давайте мыслить
дискретнопозитивно! :-)Нуууу… Скролл бывает очень дробным. Скажем проскроллил тачпадом на 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-ов.Но избегать обыкновенный
useState
этим — из пушки по воробьям. Лаконичность и простота кодовой базы сильно важнее.