- Заглушка при первой загрузке. Любой компонент в момент загрузки показывает опционально настраиваемую дефолтную заглушку.
- Автоматическая плавность перехода между перезагрузками компонента. Без isPending и подобных проверок в прикладном коде. Если первый раз уже грузили состояние, то последующая загрузка должна приводить к лёгкому приглушению и переключению на новый текст, вместо переключения на заглушку и переключению на новый текст
- Абстракция от асинхронности в прикладном коде. Не нужны эффекты с fetch, async/await, не нужно поднимать дозагрузку деталей стейта в угоду технической реализации работы с асинхронностью
- Ленивость из коробки, как, например, computed в mobx вычислится только в момент доступа к нему (что если поместить fetch в computed).
- Автоматизация саспенс-кэша, когда закэшированные данные, полученные через fetch и React.Suspense, очищаются при отмонтировании компонента, экономя память и уменьшая вероятность утечки
- Без оберток, React.Suspense в прикладном коде. Асинхронная природа данных полностью вынесена за "сцену". Это касается всех предыдущих пунктов
1.1. React.Suspense требует в месте вызова оборачивать дочерний компонент в Suspense, т.е. надо заранее знать бросит ли промис дочерний компонент или оборачивать вообще каждый компонент в Suspense, что жрет ресурсы и требует патчинга React.createElement
1.2. React.Suspense очищает стейт компонента во время бросания промиса. Если есть закэшеное тяжелое вычисление или фетч, то оно уничтожится после саспенса. Если создать, например, mobx модель и сохранить ее инстанс в useRef, то когда компонент засаспендится, ref уничтожится. Если в этой модели был кэш фетча, то будет новый запрос и так бесконечно. Поэтому react-loader хранит кэш глобально, в качестве ключа используя url. Такой кэш не очищается, когда становится не нужным (т.е. компонент его юзающий, отмонтировался с концами). Ключ задает человек, что может приводить к багам. Долго хранить кэш - черевато логическими багами из-за расхождения стейтов бэка и фронта и утечками памяти.
В случае вышеприведенных хотелок, приходится делать обертку над React.Suspense, которая бы хранила стейт и через контекст передавала дочернему, но хуки реакта так не сохранишь, только то, что явно знает об этом контексте, например стейт своей либы.
1.3. Отписка реактивных атомов во время перерендеров. В случае использования mobx, cellx, act, mutable, mol_wire и похожих либ, где реактивная связь образуется на атоматических подписках через глобальный контекст, а отписка (и сброс кэша свойств) автоматизируется через изменения источника (реактивной переменной) и последующего отсутствие перерендера любого потребителя (компонента) в том же тике. Например компонент A создал модель и делегировал кидающее промис свойство name компоненту B, если компонент B перезагрузится, то кэш в name сбросится (при условии что кэш не глобальный, как в react-fetch, а умный и уничтожится при отсутствии потребителей)
class User {
static id = 1
static name() { return myFetch('/user/123') }
}
function A() {
return <React.Suspense children={<B name={User.name} />} />
}
function B({ name }) {
return <div>{
name() // Как только бросит Promise, B отпишется от name, т.к. владеет только B
}</div>
}
Можно попробовать каждый компонент в системе оборачивать (через патч createElement) во что-то такое:
const hocComponent = Component => {
const Memoized = React.memo(Component)
return props => <React.Suspense><Memoized {...props} /></React.Suspense>
}
2.1. На сервере ведет к нетривиальному циклическому рендеру с подсчетом промисов, отказу от стримовых ssr-фишек 18го реакта.
2.2. Что отдавать в catch. Попытка в catch рисовать предыдущую vdom ноду с блендой поверх, ведет к проблемам с реконсиляцией. Внутри этой вдом ноды могут быть компоненты, которые начнут рендериться и дергать другие саспенсы. Я пробовал так: Показали заглушку, загрузили A, сохранили vdom, повторно загружаем A, AComponent в render ловит в catch Promise и рисует предыдущий vdom, оборачивая в div с блендой.
2.3. Если отдавать универсальную заглушку-пустышку во время повторной загрузки - то это ведет к лишним Content Layout Shift
2.4. Можно попробовать все оборачивать в memo, что б небыло перерендеров сабкомпонентов в catch. В условиях мутабельных объектов React.memo сломает рендер в других случаях
2.5. Еще нельзя гарантировать, что все компоненты будут хокнуты должны образом, т.к. компоненты из сторонних либ, могут асинхронно подгружены к примеру
3.1 transition может сглаживать переходы только при первой загрузке и только если загрузка инициирована из экшена или эффекта, а не при первом рендере компонента с саспенсом. В примере https://codesandbox.io/s/hardcore-mcnulty-yi1x11?file=/App.js Если кнопку убрать и сделать загрузку сразу - будет лоадер, если после первой загрузки нажать кнопку, инициировав обновление данных - опять будет лоадер.