Pull to refresh
30
0
Александр @cherkalexander

Пользователь

Send message

⚛️ React 19 — useOptimistic

useOptimistic — новый хук, который позволяет отобразить “оптимистичное” состояние. Оно называется “оптимистичным”, потому что мы надеемся, что запрос не свалится с ошибкой и после его выполнения состояние будет выглядеть именно так.

❓Как используется

  • В useOptimistic передаётся реальное состояние и функцию-reducer

  • Компонент использует “оптимистичное” состояние для рендера

  • Перед выполнением запроса обновляется “оптимистичное” состояние

  • Когда запрос завершился, нужно обновить реальное состояние

  • Как только реальное состояние обновилось, оптимистичное состояние обновится автоматически, так как оно передано в useOptimistic первым параметром.

  • Если запрос упал с ошибкой, нужно откатить изменения в оптимистичном состоянии.

ℹ️ Первый вопрос, которым я задался, а в чём отличие от обычного setState, путём экспериментов, вот что удалось найти:

  • useOptimistic работает с формами. Работать с обычной кнопкой в SPA мне не удалось, обновление происходило только после завершения запроса

  • useOptimistic работает только внутри асинхронного обработчика, что логично. Если убрать async/await, обновление произойдёт только после завершения запроса

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

В любом случае, пока useOptimistic выглядит каким-то низкоуровневым API. Надеюсь скоро появится больше Best Practices.

https://t.me/cherkashindev/184

Tags:
Total votes 4: ↑3 and ↓1+2
Comments7

⚛️ React 19 — use(Promise)

use — новый хук, который позволяет считывать данные из промиса и при этом интегрирован с Suspense и ErrorBoundary.

Основные моменты:

  • На этот хук не распространяются правила хуков — его можно использовать внутри циклов и условных операторов.

  • Если мы используем хук use(Promise), то где-то в родительском компоненте мы должны положить сам промис (не данные как мы делали раньше) в стейт (useState). Это позволяет избавиться от useEffect’а, который был нужен, чтобы запросить данные при первом рендере.

  • Хук интегрирован с Suspense, поэтому пока промис не разрезолвится — будет показан fallback объявленный в ближайшем Suspense.

  • Если промис зареджектился, то будет показан fallback объявленный в ближайшем ErrorBoundary.

Материалы

https://t.me/cherkashindev/182

Tags:
Total votes 2: ↑2 and ↓0+2
Comments0

React — Compound Components

Как-то я уже упоминал паттерн Compound Components (Составные компоненты) для React, теперь остановимся на нём немного подробнее.

ℹ️ Compound components — это подход позволяет объединить несколько компонентов в единую сущность, которая неявно имеет общее состояние. Эти компоненты тесно взаимодействуют друг с другом и работают как единое целое, представляя собой полноценный UI компонент.

? Основные характеристики:

  • Используется React контекст, чтобы управлять состоянием

  • Должен быть главный компонент, в котором хранится состояние и объявляется React контекст

  • Все дочерние компоненты используют состояние через React контекст

ℹ️ Он состоит из 2 простых подходов React:

  1. Композиция компонентов

  2. Паттерн “Провайдер” — использование контекста React 

? Вначале рассмотрим подходы по отдельности

1️⃣ Что такое композиция?

Вместо вот этого 

<Tile count={money} title="Стоимость" icon={<MoneyIcon/>}/>

Мы пишем вот так

<Tile>
  <Title>{title}</Title>
  <Number>{count}</Number>
  <Icon>{icon}</Icon>
</Tile>

2️⃣ Паттерн “Провайдер” 

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

? Если объединить два этих подхода, то сможем реализовать паттерн Compound Components. Как пример, можно использовать компонент табов из библиотеки material-ui.

https://t.me/cherkashindev/166

Tags:
Total votes 3: ↑3 and ↓0+3
Comments0

Явное управление ресурсами в TypeScript — using

Недавно, когда я рассказывал, как мы пишем тесты, я уже упоминал, что в TypeScript’е появилось новое ключевое слово using. Оно позволяет нам сделать код чище и более линейным, избавившись от try/finally. 

Но мы можем использовать using не только, когда открываем файл или подключение к базе данных, иначе его использование ограничилось бы исключительно сервером. 

Мне нравится рассматривать using, как Undo/Redo только наоборот, сперва мы выполняем какое-то действие, а в конце отменяем его:

- создали объект, удалили

- показали спиннер и скрыли, когда получили данные

Причем отмена удобно происходит в самом конце функции, даже если мы используем async/await.

Вот простой пример, как можно использовать using, чтобы показывать/скрывать спиннер в React коде.

useEffect(() => {
  (async () => {
    using manager = new LoadingManager(setIsLoading);
    await Promise.resolve().then(() => console.log("promise.resolve"));
  })();
 }, []);

/**
 * Класс, который управляет состоянием спиннера
 */
class LoadingManager {
  constructor(private setIsLoading: (value: boolean) => any) {
    this.setIsLoading(true);
    console.log("constructor");
  }

  [Symbol.dispose]() {
    this.setIsLoading(false);
    console.log("disposer")
  }
}

// В консоли будет выведено в следующем порядке
// constructor

Код можно открыть в песочнице.

К сожалению, нельзя опустить переменную manager, но это лучше, чем лепить везде try/finally.

https://t.me/cherkashindev/164

Tags:
Total votes 2: ↑1 and ↓10
Comments8

? Ещё раз об интеграции Notion и Apple Shortcuts

Пару лет назад я рассказывал, как можно использовать Apple Shortcuts, чтобы настроить быстрое добавление записей в инбокс. А в прошлом году поделился шаблоном, где для использования достаточно вставить токен notion и идентификатор базы. Но вчера я заметил, что теперь даже этого делать не нужно.

Наконец-то, Notion добавил готовые экшины для добавления записей в Inbox, всё настраивается за минуту.  Жаль, что новый способ работает только на телефоне, приложение Notion для Mac не поддерживает эти действия.

Tags:
Rating0
Comments0

Пару месяцев назад я упоминал codeium — бесплатную альтернативу копайлоту. На новогодних праздниках наконец-то попробовал его на своём пет-проекте, и довольно часто он действительно ускоряет разработку.

В текущих реалиях нужно включать VPN, чтобы работать с ChatGPT из России, здесь же есть встроенный чат, который работает без VPN ?.

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

Tags:
Total votes 6: ↑5 and ↓1+4
Comments0

Именование юнит тестов

Мне всегда нравятся “информативные” названия тестов вроде “TestLogin”. 

Смотришь и из названивая в принципе не понятно, что именно мы тестируем.

В книге “Искусство автономного тестирования (The Art of Unit Testing)” предлагается следующий способ именования тестов: [UnitOfWorkName]_[ScenarioUnderTest]_[ExpectedBehavior]

  • UnitOfWorkName — имя тестируемого метода либо группы методов или классов

  • Scenario – условия, при которых тестируется автономная единица

  • ExpectedBehavior – что должен делать метод при заданных условиях

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

Tags:
Rating0
Comments2

​​?️️️️️️ Array.prototype.sort

Как будет отсортирован следующий массив [-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4].sort()

? Предыстория

На выходных я решал литкод, и в задаче 3sum было необходимо отсортировать массив по возрастанию, перед тем как перейти к основной реализации алгоритма. 

Я написал решение, подебажил на бумаге — всё работает, отправляю код на проверку — не работает ?‍♂️. Перепроверяю всё глазами — ну должно же работать! 

Сдаюсь и начинаю дебажить в VS Code и вижу, что сортировка массива работает не так как я ожидал.

ℹ️ Объяснение

Если перейти на MDN и прочитать документацию Array.prototype.sort(), то станет всё понятно.

Метод sort() в JavaScript преобразует элементы в строки и затем сравнивает их последовательности значений кодов UTF-16. Это означает, что при сортировке числа рассматриваются как строки.

Таким образом, числа в данном случае сортируются на основе их строкового представления. Например, '-10' будет идти перед '-2', потому что строка '10' идет перед строкой '2' в лексикографическом порядке.

Чтобы выполнить числовую сортировку массива, нужно предоставить функцию сравнения методу sort(), как показано здесь:

[-1, 0, 1, 2, -1, -4, -2, -3, 3, 0, 4].sort((a, b) => a - b);

Это даст вам [-4, -3, -2, -1, -1, 0, 0, 1, 2, 3, 4] — числовую сортировку.

https://t.me/cherkashindev/136

Tags:
Total votes 7: ↑6 and ↓1+5
Comments0

Использование never в TypeScript

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

1. Явно с помощью width и height 

2. Диалог должен принять размер контента, за это отвечает свойство responsive

Тут есть несколько проблем:

1. Чтобы передать либо width и height или только responsive, мы пометили все поля как необязательные и теперь мы можем передать неправильный набор полей или не передать ни одного вовсе.

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

Нам нужно реализовать тип, в котором все поля будут обязательными, но который позволит нам передать либо width и height либо responsiv , в противном случае мы должны получить ошибку компиляции.

Такой интерфейс будет выглядеть вот так:

type DialogProps = { title: string } & (
  | {
      responsive: boolean;
      width?: never;
      height?: never;
    }
  | {

      responsive?: never;
      width: number;
      height: number;
    }

);

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

// ✅ correct 
const withSize: DialogProps = {
  title: "Dialog with Size",
  width: 600,
  height: 500,
  responsive: undefined
};

// ❌ incorrect
const incorrect: DialogProps = {
  title: "Incorrect Props Dialog",
  responsive: false,
  width: 600
  height: 500
};

В третьем примере мы получим ошибку ?.

https://t.me/cherkashindev/21

Tags:
Total votes 3: ↑3 and ↓0+3
Comments4

⚛️ Атомарные обновления в Zustand

Я уже упоминал атомарные обновления, когда говорил о проблемах контекста реакта.

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

Zustand сравнивает по ссылке предыдущее и текущее значение, возвращаемое из useStore(), и если объект изменился — происходит ре-рендер. 

❌ В следующем примере у нас всегда будет возвращаться новый объект, и ре-рендер произойдёт даже если изменился только age, а lastName и firstName не изменились

const {firstName, lastName} = useStore(({firstName, lastName}) => ({firstName, lastName}));

Аналогичное поведение будет и в следующем случае

const {firstName, lastName} = useStore();

✅ Есть три варианта использования значений из стейта, которые поддерживают атомарные обновления:

1️⃣ Одиночные селекторы

const firstName = useStore((state) => state.firstName)

const lastName = useStore((state) => state.lastName)

2️⃣ Селектор, который возвращает объект + shallow - функция сравнения предыдущего и нового стейтов

const {firstName, lastName} = useStore(({firstName, lastName}) => ({

    firstName, 

    lastName

}), shallow);

3️⃣ Автосгенерированных селекторы

const firstName = useStore.use.firstName();

const lastName = useStore.use.lastName();

Накидал небольшую демку в Codesandbox

#frontend #react #statemanagement #zustand

Tags:
Rating0
Comments0

​​?️️️️️️ Каррирование и частичное применение

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

И частичное применение, и каррирование, реализуются как функции, принимающие в качестве параметра другую функцию.

Частичное применение — функция partialApply, принимающая первым параметром функцию — fn, а остальные параметры — часть параметров функции fn. Функция partialApply возвращает функцию, которая в качестве параметров принимает недостающие аргументы функции fn.

Каррирование — функция curry, которая принимает единственный параметр — функцию fn, и возвращает каррированную функцию fn.  Можно сказать, что каррированная функция fn — функция аккумулятор, которая будет накапливать переданные аргументы до тех пор, пока не будет передано достаточно параметров для вызова исходной функции. Параметры можно передавать в любом количестве.

Подробнее

https://t.me/cherkashindev/132

Tags:
Total votes 1: ↑1 and ↓0+1
Comments4

⬇️ Проваливание промисов

Когда вы передаете в then() что-то отличное от функции (например, промис), это интерпретируется как then(null) и в следующий по цепочке промис «проваливается» результат предыдущего.

Подробнее о промисах в статье "У нас проблемы с промисами".

https://t.me/cherkashindev/131

Tags:
Total votes 2: ↑2 and ↓0+2
Comments0

?‍?Где проводить live-coding

Если вы время от времени проводите собеседования — то вы наверняка хотите не только поговорить о жизни с кандидатом, но и проверить насколько он хорош в деле. Для этого вам поможет live-кодинг. 

Для live-кодинга, вы можете использовать:

1️⃣ Yandex Code

    - ✅ Подсветка синтаксиса

    - ✅ Совместное написание кода

    - ❌ Нет подсветки ошибок

    - ❌ Нельзя запускать код

2️⃣ Interview Cups — онлайн редактор от VK

    - ✅ Подсветка синтаксиса

    - ✅ Совместное написание кода

    - ❌ Подсветка ошибок

    - ✅ Можно запускать код

3️⃣ Visual Studio Live Share. Позволяет  кандидату открыть ваш проект прямо в браузере, после чего  вы можете совместно писать код.

А что вы используете для live-кодинга?

https://t.me/cherkashindev/129

Tags:
Total votes 1: ↑1 and ↓0+1
Comments2

​​Как тестировать internal методы и классы в C# — InternalsVisibleToAttribute

[assembly: InternalsVisibleToAttribute("YourProject.UnitTests")]
[assembly: InternalsVisibleToAttribute("YourProject.UnitTests")]

Представьте, что вы разрабатываете библиотеку, которой будут пользоваться тысячи людей ?. Чтобы убедиться в стабильности — нужно всё хорошенько покрыть тестами. Все мы любим инкапсуляцию, верно (я надеюсь)? Поэтому мы не разрешаем использовать всё подряд из нашей сборки, а с умом используем модификаторы доступа и позволяем использовать только public классы и методы. 

В C#, есть 7 модификаторов доступа, основные: 

- private —  доступ только внутри текущего класса

- protected —  доступ внутри текущего и дочерних классов

- public — классы и методы доступны где угодно, также из сборок, использующих текущую

- internal — публичный API, внутри текущей сборки. Как public, но нет доступа из сборок использующих текущую

- остальные можно посмотреть тут

Но, C# — не JavaScript, и для тестов создаётся отдельная сборка, а internal методы в ней не доступны.

Чтобы тестировать internal функциональность, нужно использовать атрибут InternalsVisibleToAttribute, и в качестве параметра указать имя тестовой сборки. Тогда все internal методы и классы будут доступны для тестирования.

[assembly: InternalsVisibleToAttribute("YourProject.UnitTests")]

https://t.me/cherkashindev/127

Tags:
Total votes 4: ↑0 and ↓4-4
Comments0

Как правильно “придираться” во время код ревью

Когда вы проверяете код своего коллеги не нужно требовать от него “идеального” кода. 

ℹ️ Правило такое: вам следует апрувить изменения, если они улучшают в целом кодовую базу, даже если они не идеальны, ведь идеального кода не существует. Код можно сделать лучше чем он был до этого, но не идеальным.

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

ℹ️ В стандартах код ревью гугла, описано, что в таких ситуациях можно использовать префикс NIT, в комментариях к пул реквесту. NIT — сокращение от “nitpick” или “придираться”. 

❓В каких ситуациях стоит использовать NIT

- Чтобы выразить своё мнение или личное предпочтение. Очень важно отличать такие комментарии, от фактов. Довольно часто ревьюеры выставляют собственное мнение как истину и единственно верное решение

- Чтобы указать автору на мелкие замечания, которые не обязательно исправлять

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

Подробнее об этом можно почитать здесь:

- The Standard of Code Review


https://t.me/cherkashindev/112

Total votes 2: ↑1 and ↓10
Comments0

? Why I’ve stopped exporting defaults from my JavaScript modules

Сегодня на повестке статья из далёкого и спокойного 2019 “Why I've stopped exporting defaults from my JavaScript modules”.

В статье автор предлагает отказаться от экспортов по-умолчанию в JavaScript. Я бы выделил 2 причины, которые кажутся для меня наиболее важными:

1️⃣ Дефолтные экспорты не указывают имя функции или класса, которую вы импортируете, что может (и будет) вызывать неконсистентность в именовании. 

2️⃣ Когнитивная нагрузка замедляет разработку. Если вы используете дефолтные экспорты, вам придётся самостоятельно, каждый раз указывать название импорта. В случае с именованными экспортами ваша IDE наверняка подскажет вам имя, как только вы начнёте печатать и вам останется лишь нажать Enter.

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

? ESlint

Чтобы убедиться, что все разработчики используют именованные экспорты, вы можете использовать правило import/no-default-export 

Когда я добавлял это правило в наш проект, я наткнулся на GitLab issue, где команда Гитлаба планировали перейти с дефолтных экспортов на именованные. Они также ссылаются на эту статью.

https://t.me/cherkashindev/109

Rating0
Comments4

? What developers need to know about Chrome's Memory and Energy Saver modes - Chrome Developers

В конце прошлого года в Google Chrome появились 2 новых режима

  • Memory Saver (Экономия памяти)

  • Energy Saver (Энергосбережение)

Они позволяют более гибко управлять использованием системных ресурсов браузером.

  • Memory Saver: Этот режим автоматически освобождает неиспользуемые фоновые вкладки, чтобы освободить память для активных вкладок и других запущенных приложений. Но для сложных сайтов с интерактивностью это может привести к проблемам восстановления состояния страницы.

  • Energy Saver: Режим Energy Saver позволяет браузеру снизить частоту обновления экрана для экономии заряда батареи. Обычно, для большинства сайтов, не требуется внесение изменений, но если вы используете JavaScript-анимации, имейте в виду, что они могут замедлиться.

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

В данный момент нет никаких событий, которые будут запущены перед выгрузкой вкладки. Рекомендуются следующие способы сохранения состояния:

- Периодически, когда состояние изменяется

- Когда вы переходите на другую вкладку, по событию visibilitychange

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

Подробнее можно почитать в статье

https://t.me/cherkashindev/107

Total votes 1: ↑0 and ↓1-1
Comments0

​​Как восстановить удалённый коммит в Git?

На днях я писал базовый компонент выпадающего меню. Всё, как обычно, закончил работу и закоммитил

Потом понял, что закоммитил в мастер, а не в ветку с фичей. Поэтому сделал git reset --soft, чтобы удалить коммит, сохранив все изменённые файлы, и переключился в фича ветку. 

Если ветки сильно отличаются, то иногда приходится делать git clean -fxd, чтобы удалить все ненужные артефакты. И тут я понял, что удалил все свои изменения.

Повезло, что перед этим я закоммитил свои изменения, хотя коммит и был удалён. Ведь git позволяет восстановить удалённые коммиты. Для этого я сделал:

git reflog 

Нашёл в списке хэш моего коммита и перенёс из него все изменения в текущую ветку с помощью: git cherry-pick —no-commit <hash>

Результат работы команды git reflog
Результат работы команды git reflog

https://t.me/cherkashindev/105

Total votes 4: ↑4 and ↓0+4
Comments1

? Неделя после пуска или Cooldown week

Как часто после очередного спринта вы чувствуете, что 

- в одном месте не успели отрефакторить

- в другом месте не успели дописать документацию

- где-то осталось пару багов, которые было бы неплохо пофиксить

- нужно рассказать команде о важных (или не очень) архитектурных изменениях или базовых компонентах, которые были добавлены

Кроме того, вам в любом случае нужно время между релизами, чтобы

- подвести итог прошлого релиза

- запланировать следующий

В этом случае вам подойдёт Cooldown Week подход (Неделя после пуска), который поможет вам закрыть все недоделки (или, по крайней мере, самые важные из них), прежде чем погрузиться с головой в новый спринт.

“Неделя после пуска” — это не какой-то набор обязательных активностей, вы можете применять её в своей компании под ваши собственные нужды. 

Такой подход используется в Basecamp и в Бюро Горбунова

- Basecamp — How we work — Cooldown

- Бюро Горбунова — Как оценивать доработки после запуска?

https://t.me/cherkashindev/104

Total votes 3: ↑3 and ↓0+3
Comments0

? Apple Shortcuts для добавления в инбокс Notion

Хочу поделиться отличной находкой, связанной с Apple Shortcuts. Год назад я упоминал, что с  помощью Apple Shortcuts  можно с легкостью добавлять записи в базу ноутшена, например, я использую её для добавления записей в мой инбокс. 

Недавно я помогал настраивать систему управления проектами, похожую на ту, которую я использую сам, и тогда я задался вопросом: можно ли поделиться уже созданным шорткатом, чтобы не настраивать всё по-новой? И оказалось, что это вполне возможно! Просто нужно расшарить ссылку (но предварительно удалить свой токен и идентификатор базы).

Чтобы настроить это, вам нужно сделать всего лишь несколько шагов (скриншот в комментариях):

1. В блоке "Text" вставьте идентификатор вашей базы вместо "<your database id>".

2. В блоке "Get contents of" вставьте ваш секретный токен интеграции вместо "<your secret token>".

3. Вместо "Name" укажите название вашего поля, если оно отличается.

Я подготовил два шаблона для добавления записей, чтобы вам было проще начать использовать это:

- Команда для добавления записи текстом

- Команда для добавления записи голосом

Инструкцию по настройке команды с нуля можно найти здесь.

Если будут вопросы — пишите, будем разбираться.

https://t.me/cherkashindev/103

Rating0
Comments0
1

Information

Rating
Does not participate
Location
Курск, Курская обл., Россия
Registered
Activity