Next.js. Продвинутые опции - 2
Режим предпросмотра
Представим, что у нас есть статическая страница, которая использует getStaticProps и/или getStaticPaths и рендерится во время сборки. Например, это страница отдельного поста.
Теперь представим, что у нас есть система управления контентом, в которой мы пишем новый пост. Хотелось бы иметь возможность увидеть, как этот пост в итоге будет выглядеть - то есть иметь превью.
То есть мы хотим использовать статическую страничку поста, но динамически, передав ей текущий черновик.
Next такое может, об этом вот здесь: https://nextjs.org/docs/advanced-features/preview-mode
Автоматическая статическая оптимизация (оптимизация статических страниц)
Этот раздел про то, что Next автоматически генерирует html для страниц, которые не используют
Тут важно помнить, что если это, например, динамический роут, то страница при рендеринге не получит параметры роута, а после гидратации получит и перерендерится.
Отследить, готовы ли параметры роута, можно, проверив поле
Подробнее: https://nextjs.org/docs/advanced-features/automatic-static-optimization
Директория src
Фронтендерам привычно использовать для исходников директорию
Файлы конфигурации и директория
https://nextjs.org/docs/advanced-features/src-directory
Непрерывная интеграция и кэширование сборок
Это о том, как настроить CI workflow, чтобы кэш сохранялся между сборками: https://nextjs.org/docs/advanced-features/ci-build-caching
Multi zones
Словом zone (зона?) обозначается отдельное развертывание (деплой) Next-приложения. Таких зон может быть несколько и они могут объединяться в одно приложение.
Не совсем пока понятно, что это и для чего.
Подробнее: https://nextjs.org/docs/advanced-features/multi-zones
Измерение производительности
Next.js самостоятельно собирает все метрики производительности приложения. Чтобы использовать их, нужно из файла pages/_app.js экспортировать функцию
Подробнее: https://nextjs.org/docs/advanced-features/measuring-performance
Трассировка выходных файлов
Вроде как позволяет создать сжатую версию приложения со всеми зависимостями в отдельной папке, которую можно развернуть без node_modules.
Подробнее: https://nextjs.org/docs/advanced-features/output-file-tracing
#nextjs #документация
Режим предпросмотра
Представим, что у нас есть статическая страница, которая использует getStaticProps и/или getStaticPaths и рендерится во время сборки. Например, это страница отдельного поста.
Теперь представим, что у нас есть система управления контентом, в которой мы пишем новый пост. Хотелось бы иметь возможность увидеть, как этот пост в итоге будет выглядеть - то есть иметь превью.
То есть мы хотим использовать статическую страничку поста, но динамически, передав ей текущий черновик.
Next такое может, об этом вот здесь: https://nextjs.org/docs/advanced-features/preview-mode
Автоматическая статическая оптимизация (оптимизация статических страниц)
Этот раздел про то, что Next автоматически генерирует html для страниц, которые не используют
getServerSideProps
.Тут важно помнить, что если это, например, динамический роут, то страница при рендеринге не получит параметры роута, а после гидратации получит и перерендерится.
Отследить, готовы ли параметры роута, можно, проверив поле
router.isReady
(хук useRouter
).Подробнее: https://nextjs.org/docs/advanced-features/automatic-static-optimization
Директория src
Фронтендерам привычно использовать для исходников директорию
src
. В Next же вместо этого мы размещаем все в корне проекта. При желании, можно использовать src
, фреймворк это поддерживает. Тогда страницы должны лежать в папке src/pages
. Файлы конфигурации и директория
public
при этом должны оставаться в корне проекта.https://nextjs.org/docs/advanced-features/src-directory
Непрерывная интеграция и кэширование сборок
Это о том, как настроить CI workflow, чтобы кэш сохранялся между сборками: https://nextjs.org/docs/advanced-features/ci-build-caching
Multi zones
Словом zone (зона?) обозначается отдельное развертывание (деплой) Next-приложения. Таких зон может быть несколько и они могут объединяться в одно приложение.
Не совсем пока понятно, что это и для чего.
Подробнее: https://nextjs.org/docs/advanced-features/multi-zones
Измерение производительности
Next.js самостоятельно собирает все метрики производительности приложения. Чтобы использовать их, нужно из файла pages/_app.js экспортировать функцию
reportWebVitals
. Она будет вызываться для каждой измеренной метрики.Подробнее: https://nextjs.org/docs/advanced-features/measuring-performance
Трассировка выходных файлов
Вроде как позволяет создать сжатую версию приложения со всеми зависимостями в отдельной папке, которую можно развернуть без node_modules.
Подробнее: https://nextjs.org/docs/advanced-features/output-file-tracing
#nextjs #документация
nextjs.org
Configuring: Preview Mode
Next.js has the preview mode for statically generated pages. You can learn how it works here.
👍5
Next.js. Middleware
С помощью миддлваров мы можем вклиниться в запрос еще до того, как он достигнет нужной страницы и, например, сделать редирект в другое место. Или еще что-нибудь полезное прикрутить вроде аутентификации или AB-тестирования.
Создать такой миддлвар очень просто - нужно просто сделать файлик middleware.js и положить его в корень проекта (рядом с папкой pages). Из этого файла нужно экспортировать функцию
В качестве параметра функция принимает объект запроса и может либо вернуть объект ответа (NextResponse), либо куда-нибудь перенаправить. Если ничего не вернуть, то запрос выполнится без всяких изменений, как будто и нет никакого миддлвара.
Соответственно, объект ответа можно сформировать как угодно, проставить ему любые заголовки и т. д.
В миддлваре, например, удобно проводить проверку на аутентификацию юзера - и если ее нет, то возвращать ошибку или перенаправлять на страничку логина.
Думаю, к этой теме мы еще вернемся, звучит она интересно.
Подробнее: https://nextjs.org/docs/advanced-features/middleware
#nextjs #документация
С помощью миддлваров мы можем вклиниться в запрос еще до того, как он достигнет нужной страницы и, например, сделать редирект в другое место. Или еще что-нибудь полезное прикрутить вроде аутентификации или AB-тестирования.
Создать такой миддлвар очень просто - нужно просто сделать файлик middleware.js и положить его в корень проекта (рядом с папкой pages). Из этого файла нужно экспортировать функцию
middleware
. В качестве параметра функция принимает объект запроса и может либо вернуть объект ответа (NextResponse), либо куда-нибудь перенаправить. Если ничего не вернуть, то запрос выполнится без всяких изменений, как будто и нет никакого миддлвара.
Соответственно, объект ответа можно сформировать как угодно, проставить ему любые заголовки и т. д.
В миддлваре, например, удобно проводить проверку на аутентификацию юзера - и если ее нет, то возвращать ошибку или перенаправлять на страничку логина.
Думаю, к этой теме мы еще вернемся, звучит она интересно.
Подробнее: https://nextjs.org/docs/advanced-features/middleware
#nextjs #документация
nextjs.org
File-system conventions: middleware.js
API reference for the middleware.js file.
🔥3👍1
Next.js. Обработка ошибок
Статья в документации: https://nextjs.org/docs/advanced-features/error-handling
Тут ничего особо интересного:
development mode
В режиме разработки приложение будет просто выбрасывать модалку с описанием ошибки.
Ошибки сервера
Если ошибка произошла на стороне сервера, пользователь увидит страницу 500 (или 404, если нужная страница не найдена).
Ошибка клиента
Для обработки клиентских ошибок нам предлагают использовать предохранитель ErrorBoundary. Импортируем его в _app.js и оборачиваем в него компонент страницы.
#nextjs #документация
Статья в документации: https://nextjs.org/docs/advanced-features/error-handling
Тут ничего особо интересного:
development mode
В режиме разработки приложение будет просто выбрасывать модалку с описанием ошибки.
Ошибки сервера
Если ошибка произошла на стороне сервера, пользователь увидит страницу 500 (или 404, если нужная страница не найдена).
Ошибка клиента
Для обработки клиентских ошибок нам предлагают использовать предохранитель ErrorBoundary. Импортируем его в _app.js и оборачиваем в него компонент страницы.
#nextjs #документация
nextjs.org
Configuring: Error Handling
Handle errors in your Next.js app.
👍4
Так как я сейчас нахожусь в процессе поиска новой работы, в ближайшее время на втором канале https://yangx.top/furrycat будет несколько постов про подготовку к собеседованию по JS/React/CSS и пр. веб-технологиям - быстренько повторим основы 😌
Telegram
Cat in Web
Frontend Notes
👍2🔥1
Forwarded from Cat in Web
Материалы для подготовки к интервью
Собираю все, что набралось во время подготовки:
Самые основы JS
Типы данных https://yangx.top/furrycat/440
Объектная обертка - https://yangx.top/furrycat/450
Set/Map/WeakSet/WeakMap - https://yangx.top/furrycat/453
use strict https://yangx.top/furrycat/443
Логические операторы https://yangx.top/furrycat/439
var/let/const - https://yangx.top/furrycat/454
Хойстинг https://yangx.top/furrycat/442
Стрелочные функции - https://yangx.top/furrycat/455
Symbol - https://yangx.top/furrycat/457
Мутабельность и иммутабельность - https://yangx.top/furrycat/475
Типизация - https://yangx.top/furrycat/473
Основы посложнее
Область видимости, замыкание https://yangx.top/furrycat/441
Побитовые операторы - https://yangx.top/furrycat/458
Итераторы и генераторы - https://yangx.top/furrycat/456
Регулярные выражения - https://yangx.top/furrycat/459
Каррирование - https://yangx.top/furrycat/497
ООП, объекты
this - https://yangx.top/furrycat/444
Прототип - https://yangx.top/furrycat/445
Конструкторы - https://yangx.top/furrycat/446
Прототипное наследование - https://yangx.top/furrycat/447
instanceof - https://yangx.top/furrycat/448
Классы - https://yangx.top/furrycat/449
Объекты и их свойства - https://yangx.top/furrycat/452
Асинхронность
Асинхронность - https://yangx.top/furrycat/451
Event loop - https://yangx.top/furrycat/463
Работа в браузере
DOM - https://yangx.top/furrycat/461
События - https://yangx.top/furrycat/462
Рендеринг страницы - https://yangx.top/furrycat/470
Метрики производительности - https://yangx.top/furrycat/471
Оптимизация страницы - https://yangx.top/furrycat/472
Сети
Сети - https://yangx.top/furrycat/464
CORS - https://yangx.top/furrycat/465
Same origin policy - https://yangx.top/furrycat/466
Запросы к серверу: AJAX, JSONP - https://yangx.top/furrycat/467
Постоянная связь с сервером - https://yangx.top/furrycat/468
REST - https://yangx.top/furrycat/469
React
React. Основы - https://yangx.top/furrycat/477
React. Рефы - https://yangx.top/furrycat/478
React. Контекст - https://yangx.top/furrycat/479
Оптимизация в React - https://yangx.top/furrycat/480
Хуки - https://yangx.top/furrycat/481
Алгоритмы
Анализ сложности алгоритмов - https://yangx.top/furrycat/487
Популярные алгоритмы сортировки - https://yangx.top/furrycat/494
Алгоритмы в JS - https://yangx.top/furrycat/495
Паттерны проектирования
Паттерны проектирования - https://yangx.top/furrycat/498
Порождающие паттерны - https://yangx.top/furrycat/496
Структурные паттерны - https://yangx.top/furrycat/499
Прочее
Прогрессивное улучшение и изящная деградация - https://yangx.top/furrycat/474
SPA и PWA - https://yangx.top/furrycat/476
Функциональное программирование - https://yangx.top/furrycat/460
Примеры собесов и советы
Фронтендер в Яндекс - https://yangx.top/furrycat/486
Джун - https://yangx.top/furrycat/488
Миддл - https://yangx.top/furrycat/489
Еще миддл - https://yangx.top/furrycat/493
Алгоритмы - https://yangx.top/furrycat/502
#интервью
Собираю все, что набралось во время подготовки:
Самые основы JS
Типы данных https://yangx.top/furrycat/440
Объектная обертка - https://yangx.top/furrycat/450
Set/Map/WeakSet/WeakMap - https://yangx.top/furrycat/453
use strict https://yangx.top/furrycat/443
Логические операторы https://yangx.top/furrycat/439
var/let/const - https://yangx.top/furrycat/454
Хойстинг https://yangx.top/furrycat/442
Стрелочные функции - https://yangx.top/furrycat/455
Symbol - https://yangx.top/furrycat/457
Мутабельность и иммутабельность - https://yangx.top/furrycat/475
Типизация - https://yangx.top/furrycat/473
Основы посложнее
Область видимости, замыкание https://yangx.top/furrycat/441
Побитовые операторы - https://yangx.top/furrycat/458
Итераторы и генераторы - https://yangx.top/furrycat/456
Регулярные выражения - https://yangx.top/furrycat/459
Каррирование - https://yangx.top/furrycat/497
ООП, объекты
this - https://yangx.top/furrycat/444
Прототип - https://yangx.top/furrycat/445
Конструкторы - https://yangx.top/furrycat/446
Прототипное наследование - https://yangx.top/furrycat/447
instanceof - https://yangx.top/furrycat/448
Классы - https://yangx.top/furrycat/449
Объекты и их свойства - https://yangx.top/furrycat/452
Асинхронность
Асинхронность - https://yangx.top/furrycat/451
Event loop - https://yangx.top/furrycat/463
Работа в браузере
DOM - https://yangx.top/furrycat/461
События - https://yangx.top/furrycat/462
Рендеринг страницы - https://yangx.top/furrycat/470
Метрики производительности - https://yangx.top/furrycat/471
Оптимизация страницы - https://yangx.top/furrycat/472
Сети
Сети - https://yangx.top/furrycat/464
CORS - https://yangx.top/furrycat/465
Same origin policy - https://yangx.top/furrycat/466
Запросы к серверу: AJAX, JSONP - https://yangx.top/furrycat/467
Постоянная связь с сервером - https://yangx.top/furrycat/468
REST - https://yangx.top/furrycat/469
React
React. Основы - https://yangx.top/furrycat/477
React. Рефы - https://yangx.top/furrycat/478
React. Контекст - https://yangx.top/furrycat/479
Оптимизация в React - https://yangx.top/furrycat/480
Хуки - https://yangx.top/furrycat/481
Алгоритмы
Анализ сложности алгоритмов - https://yangx.top/furrycat/487
Популярные алгоритмы сортировки - https://yangx.top/furrycat/494
Алгоритмы в JS - https://yangx.top/furrycat/495
Паттерны проектирования
Паттерны проектирования - https://yangx.top/furrycat/498
Порождающие паттерны - https://yangx.top/furrycat/496
Структурные паттерны - https://yangx.top/furrycat/499
Прочее
Прогрессивное улучшение и изящная деградация - https://yangx.top/furrycat/474
SPA и PWA - https://yangx.top/furrycat/476
Функциональное программирование - https://yangx.top/furrycat/460
Примеры собесов и советы
Фронтендер в Яндекс - https://yangx.top/furrycat/486
Джун - https://yangx.top/furrycat/488
Миддл - https://yangx.top/furrycat/489
Еще миддл - https://yangx.top/furrycat/493
Алгоритмы - https://yangx.top/furrycat/502
#интервью
Telegram
Cat in Web
Типы данных в JS и преобразование типов
🔸Примитивные типы:
- string
- number
- bigint
- boolean
- null
- undefined
- symbol
Разница между null и undefined
undefined - это неопределенное/несуществующее значение.
- Оно по умолчанию находится в переменной…
🔸Примитивные типы:
- string
- number
- bigint
- boolean
- null
- undefined
- symbol
Разница между null и undefined
undefined - это неопределенное/несуществующее значение.
- Оно по умолчанию находится в переменной…
🔥5❤3👍2
Next.js. Индикатор загрузки страницы
Если переход с одной страницы на другую занимает некоторое время (например, подгружаются данные для второй страницы с помощью
По мотивам статьи How To Handle Loading Between Page Changes in Next.js :) (англ.) https://medium.com/@remoteupskill/how-to-manage-loading-elegantly-in-your-next-js-application-5debbfb4cace
Нам потребуется каким-то образом отслеживать начало и окончание перехода между страницами. Логично, что для этого пригодится объект роутера, который можно получить из хука
Инкапсулируем логику отслеживания в виде хука
Подписка происходит внутри хука
Теперь можно создать компонент спиннера и вывести/скрыть необходимый индикатор в зависимости от состояния
Демо: https://codesandbox.io/p/sandbox/next-page-loader-react-junior-r4n00r?file=%2Fhooks%2FuseLoading.ts&selection=%5B%7B%22endColumn%22%3A7%2C%22endLineNumber%22%3A11%2C%22startColumn%22%3A7%2C%22startLineNumber%22%3A11%7D%5D
В демо есть две странички - Home и About. Для каждой добавлена искусственная задержка загрузки (2 секунды) в
Отдельного компонента индикатора нет, он размещен вместе с прочей общей разметкой в файле _app.tsx.
#ссылки #nextjs #примерыкода
Если переход с одной страницы на другую занимает некоторое время (например, подгружаются данные для второй страницы с помощью
getServerSideProps
), имеет смысл показать пользователю какой-нибудь спиннер, чтобы он понимал, что процесс идет. Такой индикатор легко создать самостоятельно.По мотивам статьи How To Handle Loading Between Page Changes in Next.js :) (англ.) https://medium.com/@remoteupskill/how-to-manage-loading-elegantly-in-your-next-js-application-5debbfb4cace
Нам потребуется каким-то образом отслеживать начало и окончание перехода между страницами. Логично, что для этого пригодится объект роутера, который можно получить из хука
useRouter
. У роутера есть поле router.events
- это эмиттер событий. Нам нужно подписаться на событие начала перехода (routeChangeStart
) и событие окончания перехода (routeChangeComplete
).Инкапсулируем логику отслеживания в виде хука
useLoading
. Он будет возвращать состояние isLoading
, которое будет меняться в зависимости от событий роутера.Подписка происходит внутри хука
useEffect
, не забываем также про сброс эффекта.import { useEffect, useState } from "react";
import { useRouter } from "next/router";
function useLoading() {
const router = useRouter();
const [isLoading, setLoading] = useState(false);
useEffect(function() {
function onStart() {
setLoading(true);
}
function onComplete() {
setLoading(false);
}
router.events.on("routeChangeStart", onStart);
router.events.on("routeChangeComplete", onComplete);
return () => {
router.events.off("routeChangeStart", onStart);
router.events.off("routeChangeComplete", onComplete);
};
}, [router]);
return isLoading;
}
Теперь можно создать компонент спиннера и вывести/скрыть необходимый индикатор в зависимости от состояния
isLoading
.const isLoading = useLoading();
if (isLoading) return "Loading...";
return null;
Демо: https://codesandbox.io/p/sandbox/next-page-loader-react-junior-r4n00r?file=%2Fhooks%2FuseLoading.ts&selection=%5B%7B%22endColumn%22%3A7%2C%22endLineNumber%22%3A11%2C%22startColumn%22%3A7%2C%22startLineNumber%22%3A11%7D%5D
В демо есть две странички - Home и About. Для каждой добавлена искусственная задержка загрузки (2 секунды) в
getServerSideProps
, чтобы виден был индикатор.Отдельного компонента индикатора нет, он размещен вместе с прочей общей разметкой в файле _app.tsx.
#ссылки #nextjs #примерыкода
Medium
How To Handle Loading Between Page Changes in Next.js : )
If you are short of time, here is the quick version:
👍2🔥2
Стоит ли использовать Redux в Next.js-приложении?
Статья (англ.): https://javascript.plainenglish.io/should-you-use-redux-in-next-js-5e57201c34da
Автор статьи считает, что нам нужно отказаться от Redux в Next.
Причина #1: В нем нет особого смысла, так как благодаря серверному рендерингу мы сразу же, еще до рендеринга страницы, получаем нужные данные.
Причина #2: У Redux есть ряд альтернатив - более легких, нативных и удобных. Например, React Context или Local Storage. В большинстве случаев их функциональности более чем достаточно. Для более сложных сценариев, например, для загрузки данных на клиенте есть библиотеки вроде swr или react-query.
Причина #3: Redux внутри Next сложно хорошо настроить и оптимизировать.
#nextjs #ссылки #redux
Статья (англ.): https://javascript.plainenglish.io/should-you-use-redux-in-next-js-5e57201c34da
Автор статьи считает, что нам нужно отказаться от Redux в Next.
Причина #1: В нем нет особого смысла, так как благодаря серверному рендерингу мы сразу же, еще до рендеринга страницы, получаем нужные данные.
Причина #2: У Redux есть ряд альтернатив - более легких, нативных и удобных. Например, React Context или Local Storage. В большинстве случаев их функциональности более чем достаточно. Для более сложных сценариев, например, для загрузки данных на клиенте есть библиотеки вроде swr или react-query.
Причина #3: Redux внутри Next сложно хорошо настроить и оптимизировать.
#nextjs #ссылки #redux
Medium
Should You Use Redux in Next.js?
Do you even need it?
👍2
Next.js Middleware: Как оно работает и 5 примеров использования
Статья (англ.): https://javascript.plainenglish.io/next-js-middleware-how-it-works-and-5-real-use-cases-cfacbeb810c9
Про миддлвары в Next мы уже начинали говорить вот тут: https://yangx.top/react_junior/504
В качестве миддлвара выступает одна простая функция
В качестве параметра она получает запрос пользователя (
В статье разбирается базовый функционал миддлвара, а также несколько кейсов его использования:
- аутентификация: получаем данные авторизации из заголовка запроса (
- страница "Сайт находится в разработке": если в урле есть секретный параметр, то показываем реальную страницу, если нет - переадресуем всех на страницу "В разработке".
- персонализация: делим пользователей на группы, по запросу определяем, к какой группе принадлежит этот конкретный пользователь (например, по кукам), и отправляем его на соответствующую страницу.
- блокировка ботов: по заголовку
#nextjs #ссылки
Статья (англ.): https://javascript.plainenglish.io/next-js-middleware-how-it-works-and-5-real-use-cases-cfacbeb810c9
Про миддлвары в Next мы уже начинали говорить вот тут: https://yangx.top/react_junior/504
В качестве миддлвара выступает одна простая функция
middleware
, которая должна экспортироваться из файла middleware.js
в корне проекта.В качестве параметра она получает запрос пользователя (
request
) и может редиректнуть его, если необходимо.import { NextResponse } from 'next/server';Из того же файла можно экспортировать переменную
export function middleware(request) {
// redirect
return NextResponse.redirect(new URL('new_url', request.url));
// rewrite (сохранить урл, но показать другую страницу)
const url = request.nextUrl.clone();
url.pathname = 'new_url';
return NextResponse.rewrite(url);
// ничего не изменять
return NextResponse.next();
}
config
, в которой, например, можно определить для каких урлов применяется миддлвар (config.matcher
).export const config = {
matcher: '/dashboard/:path*'
}
В статье разбирается базовый функционал миддлвара, а также несколько кейсов его использования:
- аутентификация: получаем данные авторизации из заголовка запроса (
request.headers.get('authorization')
), если они не совпадают с нужными, переадресуем на страницу авторизации.- страница "Сайт находится в разработке": если в урле есть секретный параметр, то показываем реальную страницу, если нет - переадресуем всех на страницу "В разработке".
- персонализация: делим пользователей на группы, по запросу определяем, к какой группе принадлежит этот конкретный пользователь (например, по кукам), и отправляем его на соответствующую страницу.
- блокировка ботов: по заголовку
request.headers['user-agent']
определяем, является ли пользователь ботом.#nextjs #ссылки
Medium
Next.js Middleware: How it Works and 5 Real Use Cases
Next.js Middleware — how it works and 5 examples of real use cases.
👍3
Как настроить Redux в NextJS
Совсем недавно мы видели мнение, что Redux в Next.js использовать не стоит: https://yangx.top/c/1218235935/551
Но если вам все-таки хочется, то вот руководство по настройке (англ.): https://medium.com/how-to-react/how-to-setup-redux-in-nextjs-5bce0d82b8de
Выглядит не особо сложно. Помимо обычных настроек Redux нам понадобится еще функция
Теперь нужно обернуть приложение в провайдер хранилища. Сделать это лучше всего в файле _app.js. В провайдер передаем
А чтобы все работало, нужно использовать компонент высшего порядка
Вот и все, теперь можно пользоваться плюшками Redux.
#nextjs #redux #статьи
Совсем недавно мы видели мнение, что Redux в Next.js использовать не стоит: https://yangx.top/c/1218235935/551
Но если вам все-таки хочется, то вот руководство по настройке (англ.): https://medium.com/how-to-react/how-to-setup-redux-in-nextjs-5bce0d82b8de
Выглядит не особо сложно. Помимо обычных настроек Redux нам понадобится еще функция
createWrapper
из пакета next-redux-wrapper.const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
const wrapper = createWrapper(function() {
return store;
});
Теперь нужно обернуть приложение в провайдер хранилища. Сделать это лучше всего в файле _app.js. В провайдер передаем
store
.А чтобы все работало, нужно использовать компонент высшего порядка
wrapper.withRedux
.import { Provider } from "react-redux";
function MyApp({ Component, pageProps }) {
return (
Provider store={store}
Component {...pageProps}
)
}
export default wrapper.withRedux(MyApp);
Вот и все, теперь можно пользоваться плюшками Redux.
#nextjs #redux #статьи
👍3
Redux-saga. Общий обзор
Быстрый экскурс в Redux-saga: что за штука, зачем нужна, как примерно работает.
Saga - это аналог Redux-thunk, штука, которая помогает нам управлять сайд-эффектами в redux. Когда мы диспатчим в хранилище какой-то экшен и хотим, чтобы при этом запрашивались с сервера какие-то данные - нам сюда.
С thunk уже разбирались: он позволяет вместо объекта экшена создавать функцию. Миддлвар смотрит на экшен и, если видит, что это функция, не пропускаего его в стор, а вызывает, передавая аргументами полезные методы и данные стора. (самописный пример)
То есть с thunk нам необходимо усложнять action creators, всю логику мы кладем туда.
Saga работает по-другому: она "прослушивает" экшены и, если видит нужный, запускает обработчик для него. Соответственно, в саге есть вотчеры (следят за экшенами) и воркеры (содержат бизнес-логику).
А еще есть эффекты - это библиотечные функции (встроены в Saga), которые позволяют нам выстроить правильную последовательность действий внутри воркеров, в общем, утилиты.
Структура saga
Сага имеет древовидную структуру, то есть у нас есть корневая сага, которая "ветвится" - устанавливает какие-то базовые вотчеры и воркеры - дочерние саги. Каждая из дочерних саг тоже может "ветвиться". У такого подхода есть полезные плюшки - весь рабочий процесс делится на "транзакции", которые можно отменять. На это чуть позже посмотрим поближе.
Важно: саги - это функции-генераторы! То есть они могут прерывать процесс своего выполнения. Освежить память по генераторам можно, например, здесь: https://yangx.top/furrycat/456
Установка и подключение
Нам нужен пакет redux-saga. Сага подключается к redux-хранилищу как миддлвар.
Посмотреть можно здесь: https://codesandbox.io/s/redux-saga-getting-started-react-junior-h3l596?file=/src/store/index.js
- Сначала создаем миддлвар с помощью функции
- добавляем его к остальным с помощью
- передаем их в
- После создания хранилища нужно еще запустить саговский миддлвар, вызвав у него метод
#redux #управлениесостоянием #примерыкода #saga
Быстрый экскурс в Redux-saga: что за штука, зачем нужна, как примерно работает.
Saga - это аналог Redux-thunk, штука, которая помогает нам управлять сайд-эффектами в redux. Когда мы диспатчим в хранилище какой-то экшен и хотим, чтобы при этом запрашивались с сервера какие-то данные - нам сюда.
С thunk уже разбирались: он позволяет вместо объекта экшена создавать функцию. Миддлвар смотрит на экшен и, если видит, что это функция, не пропускаего его в стор, а вызывает, передавая аргументами полезные методы и данные стора. (самописный пример)
То есть с thunk нам необходимо усложнять action creators, всю логику мы кладем туда.
Saga работает по-другому: она "прослушивает" экшены и, если видит нужный, запускает обработчик для него. Соответственно, в саге есть вотчеры (следят за экшенами) и воркеры (содержат бизнес-логику).
А еще есть эффекты - это библиотечные функции (встроены в Saga), которые позволяют нам выстроить правильную последовательность действий внутри воркеров, в общем, утилиты.
Структура saga
Сага имеет древовидную структуру, то есть у нас есть корневая сага, которая "ветвится" - устанавливает какие-то базовые вотчеры и воркеры - дочерние саги. Каждая из дочерних саг тоже может "ветвиться". У такого подхода есть полезные плюшки - весь рабочий процесс делится на "транзакции", которые можно отменять. На это чуть позже посмотрим поближе.
Важно: саги - это функции-генераторы! То есть они могут прерывать процесс своего выполнения. Освежить память по генераторам можно, например, здесь: https://yangx.top/furrycat/456
Установка и подключение
Нам нужен пакет redux-saga. Сага подключается к redux-хранилищу как миддлвар.
Посмотреть можно здесь: https://codesandbox.io/s/redux-saga-getting-started-react-junior-h3l596?file=/src/store/index.js
- Сначала создаем миддлвар с помощью функции
createSagaMiddleware
, - добавляем его к остальным с помощью
applyMiddleware
, - передаем их в
createStore
. - После создания хранилища нужно еще запустить саговский миддлвар, вызвав у него метод
sagaMiddleware.run()
.#redux #управлениесостоянием #примерыкода #saga
Telegram
React Junior
Экшены-функции
Еще один вариант - переместить код, связанный с экшеном в функцию-создатель экшена. Но тут есть проблема - у функций-создателей нет доступа к методу store.dispatch.
Эту задачу тоже можно решить с помощью миддлваров. Нужно сделать так, чтобы…
Еще один вариант - переместить код, связанный с экшеном в функцию-создатель экшена. Но тут есть проблема - у функций-создателей нет доступа к методу store.dispatch.
Эту задачу тоже можно решить с помощью миддлваров. Нужно сделать так, чтобы…
👍5
Redux-saga. Структура саг и эффекты
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты и получили список уведомлений).
У нас есть корневая сага (`rootSaga`) - корневой процесс.
Сага может вызывать другие саги, формируя древовидную структуру, причем вложенные саги связаны с родительской.
Принципиальная схема саги: разделяем вотчер и воркер. Внутри них нам понадобятся "эффекты" - это очень крутая штука, которая позволяет наворачивать логику в зависимости от срабатывания экшенов в нужной для нас последовательности.
Например, чтобы "подписаться" на экшен, нужен эффект take:
Корневая сага запускает вотчер, который подписывается на экшен с типом
Эта штука сработает только один раз, при первом же экшене
Если нужно, чтобы срабатывало при каждом, потребуется другой эффект -
А внутри воркера можем получить доступ к хранилищу и вывести текущее значение счетчика с помощью эффекта
Немного усложним и представим, что воркер выполняет некую асинхронную работу, которая занимает время, например, отправляет запрос на сервер. И мы не хотим, чтобы два запроса выполнялось одновременно. Для этого есть эффекты
Кстати, для создания искусственной задержки есть эффект
Так, экшены отслеживать умеем, из стора читать умеем, надо еще диспатчить. Это тоже можно - с эффектом
Например, реализуем асинхронный инкремент. Тут нам понадобится и
Для начала, пожалуй, достаточно.
#redux #управлениесостоянием #примерыкода #saga
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты и получили список уведомлений).
У нас есть корневая сага (`rootSaga`) - корневой процесс.
Сага может вызывать другие саги, формируя древовидную структуру, причем вложенные саги связаны с родительской.
Принципиальная схема саги: разделяем вотчер и воркер. Внутри них нам понадобятся "эффекты" - это очень крутая штука, которая позволяет наворачивать логику в зависимости от срабатывания экшенов в нужной для нас последовательности.
Например, чтобы "подписаться" на экшен, нужен эффект take:
import { take } from "@redux-saga/core/effects"
function workerSaga() {
console.log('count increased')
yield;
}
function* watcherSaga() {
yield take(INCREMENT_COUNTER);
yield workerSaga();
}
function* rootSaga() {
yield watcherSaga();
}
Корневая сага запускает вотчер, который подписывается на экшен с типом
INCREMENT_COUNTER
. Пока этот экшен не сработает, остальной код в вотчере НЕ вызовется. То есть мы будем ждать экшена и только потом запустим воркер.Эта штука сработает только один раз, при первом же экшене
INCREMENT_COUNTER
.Если нужно, чтобы срабатывало при каждом, потребуется другой эффект -
takeEvery
:
function* watcherSaga() {
yield takeEvery(INCREMENT_COUNTER, workerSaga)
}
А внутри воркера можем получить доступ к хранилищу и вывести текущее значение счетчика с помощью эффекта
select
:
function* workerSata() {
const count = yield select((state) => state.counter.value);
console.log("count increased", count);
}
Немного усложним и представим, что воркер выполняет некую асинхронную работу, которая занимает время, например, отправляет запрос на сервер. И мы не хотим, чтобы два запроса выполнялось одновременно. Для этого есть эффекты
takeLatest
и takeLeading
- используем их вместо takeEvery
. takeLeading
не запускает воркер заново, пока выполняется предыдущий.takeLatest
отменяет текущий запрос, если пришел новый экшен.Кстати, для создания искусственной задержки есть эффект
delay
.Так, экшены отслеживать умеем, из стора читать умеем, надо еще диспатчить. Это тоже можно - с эффектом
put
. Например, реализуем асинхронный инкремент. Тут нам понадобится и
delay
, и takeLatest
, и put
.
function* asyncIncrementSaga() {
yield delay(1000);
yield put({ type: INCREMENT_COUNTER });
}
function* watcher() {
yield takeLatest(INCREMENT_COUNTER_ASYNC, asynIncrementSaga)
}
Для начала, пожалуй, достаточно.
#redux #управлениесостоянием #примерыкода #saga
👍4
React: самые используемые типы
Статья (англ.): https://jser.dev/2023-05-31-react-types-in-typescript/
Автор статьи сделал подборку типов, используемых в React, чтобы нам не пришлось.
1. ReactElement
Главный "строительный" метод React - React.createElement(). Он принимает конфиг элемента, который нужно создать - его и описывает интерфейс ReactElement.
ReactElement или JSX.Element (то же самое, просто алиас) - это то, что должно быть возвращено из JSXElementConstructor (из функционального или классового компонента).
2. ReactNode
Это "надтип" для ReactElement, как и в DOM - есть ноды (включают комментарии и текстовые узлы), а есть их подмножество - элементы.
В тип ReactNode входят ReactElement, строки, числа, булевы значения, фрагменты, порталы и даже null и undefined
3. FunctionComponent
Интерфейс, описывающий функциональный компонент
4. RefObject и MutableRefObject
Типы, описывающие рефы, созданные хуком
У
5. ComponentProps
Обобщенный тип, позволяющий "извлечь" из типа компонента его пропсы. Под капотом использует infer.
6. Dispatch и SetStateAction
Эти типы описывают самый популярный хук
Тип
7. События
С типизацией событий в React всегда сложно. Есть два подхода:
- типизировать само событие (SyntheticEvent), которое приходит в обработчик в виде аргумента, например, MouseEvent
- или типизировать сам обработчик - MouseEventHandler
Оба типа - дженерики, которым можно передать тип элемента, на котором происходит событие.
#typescript #ссылки
Статья (англ.): https://jser.dev/2023-05-31-react-types-in-typescript/
Автор статьи сделал подборку типов, используемых в React, чтобы нам не пришлось.
1. ReactElement
Главный "строительный" метод React - React.createElement(). Он принимает конфиг элемента, который нужно создать - его и описывает интерфейс ReactElement.
interface ReactElement {
type: T,
props: P,
key: Key | null,
}
ReactElement или JSX.Element (то же самое, просто алиас) - это то, что должно быть возвращено из JSXElementConstructor (из функционального или классового компонента).
2. ReactNode
Это "надтип" для ReactElement, как и в DOM - есть ноды (включают комментарии и текстовые узлы), а есть их подмножество - элементы.
В тип ReactNode входят ReactElement, строки, числа, булевы значения, фрагменты, порталы и даже null и undefined
3. FunctionComponent
Интерфейс, описывающий функциональный компонент
4. RefObject и MutableRefObject
Типы, описывающие рефы, созданные хуком
useRef
. У
useRef
есть несколько перезагрузок и одна из них приводит к тому, что поле ref.current
становится readonly
и его нельзя изменить 😳5. ComponentProps
Обобщенный тип, позволяющий "извлечь" из типа компонента его пропсы. Под капотом использует infer.
6. Dispatch и SetStateAction
Эти типы описывают самый популярный хук
useState
.Тип
Dispatch
- это просто функция, которая принимает аргумент заявленного типа и ничего не возвращает.SetStateAction
- это либо новое состояние, либо функция, которая принимает старое состояние и возвращает новое.7. События
С типизацией событий в React всегда сложно. Есть два подхода:
- типизировать само событие (SyntheticEvent), которое приходит в обработчик в виде аргумента, например, MouseEvent
- или типизировать сам обработчик - MouseEventHandler
Оба типа - дженерики, которым можно передать тип элемента, на котором происходит событие.
#typescript #ссылки
jser.dev
React types in TypeScript
Demystify the common React types in TypeScript
👍3
Redux-saga. Эффект call и тестирование саг
Продолжаем разбор redux-saga.
В прошлый раз мы узнали:
- что саги объединяются в виде дерева - из одной саги могут "прорастать" другие. Такая структура позволяет при ошибке изолировать проблемную ветку, об этом поговорим чуть позже.
- что саги делятся на вотчеры (подписка на события) и воркеры (обработчики событий).
- что у нас есть куча встроенных хелперов (эффектов) и для вотчеров (take/takeEvery/takeLatest/takeLeading), и для воркеров (delay/select/put). Эффекты - это обычные объекты, содержащие инструкции для миддлвара.
Теперь чуть поближе посмотрим на эффекты на примере эффекта
Call
А можем через
Функциональной разницы нет, запрос и так, и так будет выполнен. Но есть разница техническая.
Например, мы хотим протестировать сагу
В первом случае в переменной
Эффект возвращает нам просто объект с инструкциями, который проще тестировать. А сам вызов произойдет уже в миддлваре. Для тестирования мы можем просто снова создать эффект с теми же параметрами:
При таком подходе нам не придется подменять метод API.fetch, так как мы его даже не вызываем.
Контекст выполнения
В
Apply
Кроме
#redux #управлениесостоянием #примерыкода #saga
Продолжаем разбор redux-saga.
В прошлый раз мы узнали:
- что саги объединяются в виде дерева - из одной саги могут "прорастать" другие. Такая структура позволяет при ошибке изолировать проблемную ветку, об этом поговорим чуть позже.
- что саги делятся на вотчеры (подписка на события) и воркеры (обработчики событий).
- что у нас есть куча встроенных хелперов (эффектов) и для вотчеров (take/takeEvery/takeLatest/takeLeading), и для воркеров (delay/select/put). Эффекты - это обычные объекты, содержащие инструкции для миддлвара.
Теперь чуть поближе посмотрим на эффекты на примере эффекта
call
и немного затронем тему тестирования саг.Call
Call
- встроенный эффект, который работает как стандартный метод Function.call
. Он принимает функцию, которую нужно вызывать и набор аргументов для вызова. То есть мы можем вызвать функцию напрямую:
function* fetchProducts() {
const products = yield API.fetch('/products');
}
А можем через
call
:
function* fetchProducts() {
const products = yield call(API.fetch, '/products');
}
Функциональной разницы нет, запрос и так, и так будет выполнен. Но есть разница техническая.
Например, мы хотим протестировать сагу
fetchProducts
. Это генератор, поэтому нужно использовать метод next
для получения каждого промежуточного результата:
const iterator = fetchProducts();
const res = iterator.next();
В первом случае в переменной
res
окажется промис. Во втором - объект эффекта, который выглядит примерно так:
{
CALL: {
fn: Api.fetch,
args: ['./products']
}
}
Эффект возвращает нам просто объект с инструкциями, который проще тестировать. А сам вызов произойдет уже в миддлваре. Для тестирования мы можем просто снова создать эффект с теми же параметрами:
const { call } from 'redux-saga/effects';
const iterator = fetchProducts();
const res = iterator.next();
assert.deepEqual(
res.value,
call(API.fetch, '/products'),
"fetchProducts should yield an Effect call(API.fetch, './products')"
)
При таком подходе нам не придется подменять метод API.fetch, так как мы его даже не вызываем.
Контекст выполнения
В
call
можно передать контекст выполнения:
yield call([obj, obj.method], arg1, arg2, ...)
Apply
Кроме
call
есть еще эффект apply
, который принимает аргументы в виде массива:
yield apply(obj, obj.method, [arg1, arg2, ...])
call
и apply
удобно использовать для функций, которые возвращают промис. Если нужно вызвать функцию в node-стиле, которая принимает последним параметром коллбэк, есть специальный эффект cps
.#redux #управлениесостоянием #примерыкода #saga
Telegram
React Junior
Redux-saga. Структура саг и эффекты
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты…
Итак, сага создает некие процессы. Они могут быть последовательные, зависящие друг от друга, (сначала получили id пользователя, потом для этого id список постов) и параллельные, не зависящие друг от друга (получили посты…
👍3🔥1
Redux-saga. Блокирующие и неблокирующие эффекты. Call vs Fork
В redux-saga есть прекрасный эффект
Логика у него довольно простая, попробуем реализовать его своими силами с помощью уже знакомых простых эффектов
Тут мы запускаем бесконечный цикл, ждем, когда произойдет событие и вызываем обработчик для него.
Но этот код будет работать не так, как предполагается. Догадались, почему?
Эффект
- произошло событие
- запустилась сага
- выполнение
- когда
Если событие
Чтобы это починить, нужно заменить блокирующий эффект
Демонстрация разницы: https://codesandbox.io/s/redux-saga-call-vs-fork-react-junior-8yhf4l?file=/src/app/saga.js
Важно понять, что с неблокирующими эффектами мы не можем использовать, например, блок try-catch, так как выполнение неблокирующей саги происходит вне контекста родительской саги - в "конкурентном" режиме.
#redux #управлениесостоянием #примерыкода #saga
В redux-saga есть прекрасный эффект
takeEvery
, который позволяет подписаться на каждый вызов события. Логика у него довольно простая, попробуем реализовать его своими силами с помощью уже знакомых простых эффектов
take
и call
:
function* worker() {
yield delay(1000);
yield console.log('click');
}
function* watcher() {
while (true) {
yield take('CLICK');
yield call(worker);
}
}
Тут мы запускаем бесконечный цикл, ждем, когда произойдет событие и вызываем обработчик для него.
Но этот код будет работать не так, как предполагается. Догадались, почему?
Эффект
call
является блокирующим. Это значит, что когда он запускается, функция-генератор приостанавливает свою работу, ждет, когда он выполнится, и только потом возобновляет свое выполнение. - произошло событие
CLICK
- запустилась сага
worker
- выполнение
worker
занимает некоторое время, так как там тоже есть блокирующий эффект delay
- когда
worker
выполнился (минимум через 1 секунду), продолжается выполнение watcher
- цикл идет на следующий кругЕсли событие
CLICK
поступит во время выполнения саги worker
, его просто никто не заметит. То есть логика эффекта takeEvery
не повторяется.Чтобы это починить, нужно заменить блокирующий эффект
call
на неблокирующий fork
. Он создаст отдельную "ветку" для новой саги, а выполнение родительской саги сразу же продолжится.
function* watcher() {
while (true) {
yield fork('CLICK');
yield call(worker);
}
}
Демонстрация разницы: https://codesandbox.io/s/redux-saga-call-vs-fork-react-junior-8yhf4l?file=/src/app/saga.js
Важно понять, что с неблокирующими эффектами мы не можем использовать, например, блок try-catch, так как выполнение неблокирующей саги происходит вне контекста родительской саги - в "конкурентном" режиме.
#redux #управлениесостоянием #примерыкода #saga
CodeSandbox
Redux-saga. Call vs Fork. React Junior - CodeSandbox
Redux-saga. Call vs Fork. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-scripts, redux-saga
👍4
Redux-saga. Отмена эффекта
Теперь попробуем повторить поведение встроенного эффекта
Помним, что все функции эффектов возвращают простой объект эффекта. Именно этот объект и нужно передать в
Демо здесь: https://codesandbox.io/s/redux-saga-cancel-react-junior-hlx6hh?file=/src/app/saga.js
#redux #управлениесостоянием #примерыкода #saga
Теперь попробуем повторить поведение встроенного эффекта
takeLatest
. Помимо того, что нам нужно отслеживать событие и запускать для него воркер, необходимо еще останавливать ранее запущенные воркеры. Для этого есть эффект cancel
.Помним, что все функции эффектов возвращают простой объект эффекта. Именно этот объект и нужно передать в
cancel
, чтобы отменить эффект.
let unique = 1;
export function* clickWorker(counter) {
yield delay(1000);
yield console.log("click", counter);
}
export function* clickWatcher() {
let lastEffect;
while (true) {
yield take("CLICK");
if (lastEffect) {
yield cancel(lastEffect);
}
lastEffect = yield fork(clickWorker, unique1++);
}
}
Демо здесь: https://codesandbox.io/s/redux-saga-cancel-react-junior-hlx6hh?file=/src/app/saga.js
#redux #управлениесостоянием #примерыкода #saga
CodeSandbox
Redux-saga. Cancel. React Junior - CodeSandbox
Redux-saga. Cancel. React Junior by furrycat.web using @reduxjs/toolkit, react, react-dom, react-redux, react-scripts, redux-saga
👍3
Redux-saga. Отмена эффекта (продолжение)
Мы научились запускать неблокирующий эффект
Пример из документации - фоновое сохранение данных, например, текста поста в админке вашего сайта. При событии
Для этого в форкнутой саге нужно использовать эффект
Эффект
#redux #управлениесостоянием #примерыкода #saga
Мы научились запускать неблокирующий эффект
fork
и затем при необходимости отменять его с помощью cancel
. Но что если до отмены наша форкнутая сага успела что-то сделать, например, отправила запрос на сервер. При отмене саги было бы хорошо отменить и этот запрос.Пример из документации - фоновое сохранение данных, например, текста поста в админке вашего сайта. При событии
START_BACKGROUND_SYNC
запускается процесс синхронизации - запрос отправляется раз в какое-то время. При событии STOP_BACKGROUND_SYNC
нужно этот процесс остановить, а также отменить последний отправленный запрос.Для этого в форкнутой саге нужно использовать эффект
cancelled
+ try-finally.
function* bgSync() {
try {
while (true) {
// получаем нужные данные/отправляем нужные запросы
// повторяем каждые 5 секунд
yield delay(5000)
}
} finally {
// блок сработает, если сага была отменена
if (yield cancelled())
// логика отмены
}
}
function* main() {
// ждем события начала синхронизации
while ( yield take('START_BACKGROUND_SYNC') ) {
// форкаем сагу с логикой синхронизации
const bgSyncTask = yield fork(bgSync)
// ждем события конца синхронизации
yield take('STOP_BACKGROUND_SYNC')
// отменяем синхронизацию
yield cancel(bgSyncTask)
}
}
Эффект
cancel
останавливает работу генератора, поэтому он перепрыгивает сразу в блок finally
. Тут мы и проверяем, вызвано ли завершение работы тем, что произошла отмена.#redux #управлениесостоянием #примерыкода #saga
redux-saga.js.org
Task Cancellation | Redux-Saga
We saw already an example of cancellation in the Non blocking calls section. In this section we'll review cancellation in more detail.
🔥3
Redux-saga. Прикрепление саг к родителю
Наконец-то дошли до архитектуры - посмотрим, зачем нам нужно такое дерево саг.
Мы уже знакомы с эффектом
-
-
То есть разница заключается в наличии связи с "родительской" сагой, которая создала новую ветку.
- Сага является завершенной, если завершено ее собственное тело, а также все прикрепленные к ней ветки.
- Если в прикрепленной ветке возникает ошибка, то она всплывает вверх - к родительской саге (и дальше, если ее не обработать).
- При отмене саги с помощью эффекта
Важно: обработка ошибки в блоке try-catch возможна только для блокирующих вызовов (эффект
Ветки, созданные с помощью
#redux #управлениесостоянием #saga
Наконец-то дошли до архитектуры - посмотрим, зачем нам нужно такое дерево саг.
Мы уже знакомы с эффектом
fork
, который позволяет запускать новую ветку саг, не блокируя выполнение родителя. А есть еще spawn
. В чем между ними разница?-
fork
(от слова 'вилка') создает новую ветку, которая "прикреплена" (attached) к родителю.-
spawn
(от слова 'порождать') создает новую "отделенную" (detached) ветку.То есть разница заключается в наличии связи с "родительской" сагой, которая создала новую ветку.
- Сага является завершенной, если завершено ее собственное тело, а также все прикрепленные к ней ветки.
- Если в прикрепленной ветке возникает ошибка, то она всплывает вверх - к родительской саге (и дальше, если ее не обработать).
- При отмене саги с помощью эффекта
cancel
, отменяются также все прикрепленные к ней ветки.Важно: обработка ошибки в блоке try-catch возможна только для блокирующих вызовов (эффект
call`). Отдельные ветки (`fork
`, `spawn`) выполняются асинхронно, поэтому с ними такое не сработает.Ветки, созданные с помощью
spawn
, не имеют никакой связи со своим родителем и похожи больше на отдельные "корневые" саги - таким образом создаются новые ветки в эффекте takeEvery
.#redux #управлениесостоянием #saga
❤2🔥1
Redux-saga. Композиция саг
👉 All
Чтобы запустить несколько саг параллельно, используем эффект
Идея такая же, как в
👉 Race
Есть и аналог
Тут мы ждем, пока выполнится запрос, но не дольше одной секунды. Если запрос успеет выполниться, то его результаты придут в переменную
Удобно сочетать
Не следует использовать эффект
#redux #управлениесостоянием #примерыкода #saga
👉 All
Чтобы запустить несколько саг параллельно, используем эффект
all
:
const results = yield all([call(task1), call(task2), ...]);
yield put(showResults(results));
Идея такая же, как в
Promise.all
- дожидаемся, пока все саги в массиве выполнятся, и продолжаем исполнять код дальше.👉 Race
Есть и аналог
Promise.race
- эффект race
, но с немного другим синтаксисом:
const {posts, timeout} = yield race({
posts: call(fetchApi, '/posts'),
timeout: delay(1000)
})
if (posts)
yield put({type: 'POSTS_RECEIVED', posts})
else
yield put({type: 'TIMEOUT_ERROR'})
Тут мы ждем, пока выполнится запрос, но не дольше одной секунды. Если запрос успеет выполниться, то его результаты придут в переменную
posts
.Удобно сочетать
race
с эффектом cancel
, чтобы отменять "проигравшие" гонку саги.Не следует использовать эффект
fork
внутри race
, так как он неблокирующий и поэтому всегда будет "выполняться" первым.#redux #управлениесостоянием #примерыкода #saga
🔥1
Redux-saga. Корневая сага
Еще пару слов об организации корневой саги с учетом новых знаний про блокирующие/неблокирующие эффекты, а также про attached/detached ветки.
В корневой саге обычно запускается несколько дочерних саг и предполагается, что это происходит одновременно. Поэтому нам нужен неблокирующий способ запуска - есть несколько вариантов.
🔹Параллельный запуск с all
Тут нужно учитывать, что сам эффект all блокирующий и если после него идет какой-то код, то он будет выполнен только после завершения всех саг в массиве.
🔸Неблокирующие вызовы с fork
🔹All + fork
Чтобы решить проблему блокирования корневой саги эффектом all, можно сочетать его с fork, чтобы вызовы всех саг были неблокирующими:
Так все саги запустятся параллельно, но при этом all не будет ждать, пока все они выполнятся.
При этом все созданные ветки остаются привязанными к корневой саге, поэтому если одна из них упадет с ошибкой, корневая сага тоже упадет.
🔸Отделенные ветки со spawn
Не самое популярное решение, но тоже имеет место быть. Можно запустить дочерние саги внутри корневой с помощью spawn. Тогда они будут отделены от родителя и их ошибки не повляют на работу корневой саги.
🔹Перезапуск при падении
И еще один примерчик на сладкое - возможность перезапуска саги, если она не завелась:
Ветки создаются с помощью spawn, чтобы не влиять на родителя и не блокировать его выполнение.
Каждая дочерняя сага запускается с помощью блокирующего эффекта call, поэтому мы можем использовать блок try-catch для обработки ошибок. Если сага удачно запустилась, то на этом все и заканчивается, если же нет, то происходит новый виток бесконечного цикла while(true) и выполняется новая попытка.
В целом все эти примеры актуальны не только для корневой саги, но и для любых других.
#redux #управлениесостоянием #примерыкода #saga
Еще пару слов об организации корневой саги с учетом новых знаний про блокирующие/неблокирующие эффекты, а также про attached/detached ветки.
В корневой саге обычно запускается несколько дочерних саг и предполагается, что это происходит одновременно. Поэтому нам нужен неблокирующий способ запуска - есть несколько вариантов.
🔹Параллельный запуск с all
function* rootSaga() {
yield all([
saga1(),
saga1()
])
}
Тут нужно учитывать, что сам эффект all блокирующий и если после него идет какой-то код, то он будет выполнен только после завершения всех саг в массиве.
🔸Неблокирующие вызовы с fork
yield fork(saga1)
yield fork(saga2)
yield fork(saga3)
🔹All + fork
Чтобы решить проблему блокирования корневой саги эффектом all, можно сочетать его с fork, чтобы вызовы всех саг были неблокирующими:
yield all([ fork(saga1), fork(saga2), fork(saga3) ])
Так все саги запустятся параллельно, но при этом all не будет ждать, пока все они выполнятся.
При этом все созданные ветки остаются привязанными к корневой саге, поэтому если одна из них упадет с ошибкой, корневая сага тоже упадет.
🔸Отделенные ветки со spawn
Не самое популярное решение, но тоже имеет место быть. Можно запустить дочерние саги внутри корневой с помощью spawn. Тогда они будут отделены от родителя и их ошибки не повляют на работу корневой саги.
yield spawn(saga1)
yield spawn(saga2)
yield spawn(saga3)
🔹Перезапуск при падении
И еще один примерчик на сладкое - возможность перезапуска саги, если она не завелась:
const sagas = [
saga1,
saga2,
saga3,
];
yield all(sagas.map(saga =>
spawn(function* () {
while (true) {
try {
yield call(saga)
break
} catch (e) {
console.log(e)
}
}
}))
);
Ветки создаются с помощью spawn, чтобы не влиять на родителя и не блокировать его выполнение.
Каждая дочерняя сага запускается с помощью блокирующего эффекта call, поэтому мы можем использовать блок try-catch для обработки ошибок. Если сага удачно запустилась, то на этом все и заканчивается, если же нет, то происходит новый виток бесконечного цикла while(true) и выполняется новая попытка.
В целом все эти примеры актуальны не только для корневой саги, но и для любых других.
#redux #управлениесостоянием #примерыкода #saga
👍2