Короткие и полезные заметки про React
function App() {
return (
<Parent>
<Child />
</Parent>
);
}
function Parent({children}) {
const [stateA, dispatchA] = useReducer(reducerA, initialStateA);
return (
<div className="parent">
<Context.Provider value={[stateA, dispatchA]}>{children}</Context.Provider>
</div>
);
}
В примере выше при каждом повторном рендере Parent будет происходить повторный рендер потребителей контекста, т.к. изменилась ссылка на значение. Самый легкий способ это исправить - это обернуть значение в useMemo. Однако это избыточно для компонентов в верху дерева компонентов. В примере выше Parent находится в самом верху дерева и выше него нет других компонентов, которые могли бы спровоцировать повторный рендер. Таким образом использование функции useMemo в примере выше бесполезна.
https://www.zhenghao.io/posts/top-level-perf
const Button = ({ title, onClick }) => <button onClick={onClick}>{title}</button>;
И компоненты контейнеры:
const Button = ({ children, onClick }) => <button onClick={onClick}>{children}</button>;
У компонента контейнера вместо пропса title проп children. Это различие позволяет более гибко использовать компонент контейнер и передавать в children любые элементы, при этом синтаксис выглядит как использование обычного HTML тега.
Можно определить следующие правила для декомпозиции компонента:
- Начинайте реализацию “сверху вниз”. Если делать “снизу вверх”, т.е. сначала создавать небольшие переиспользуемые компоненты, то возможно у вас получится компонент со слишком сложным API и будет отсутствовать половина нужного функционала.
- Создавайте подкомпонент только тогда, когда в этом есть необходимость. Одно из возможных причин, когда компонент нужно декомпозировать – это его размер.
- Начинайте композицию с “простых” компонентов, а другие подходы композиции используйте по мере необходимости.
Также стоит отметить про принцип Единый Уровень Абстракции (Single Level of Abstraction).
www.developerway.com/posts/c…it-right@types/react@^18.0.0. В нем есть несколько критических изменений:
- Убран неявный проп children в React.FC и React.Component. Теперь, если компонент ожидает проп children, то его нужно передавать явно:
interface Props {
children?: React.ReactNode;
}
const SomeFunctionComponent: React.FC<Props> = props => <div>{props.children}</div>
Есть несколько причин, почему был удален проп children. Во первых, очень часто этот проп не использовался. Но так как он был объявлен в типах, то это могло приводить к неверным предположениям о работе компонента (раз есть проп children, то значит он как-то используется внутри компонента). Во вторых, более строгая типизация пропа children, теперь можно самостоятельно указать его тип. Более подробно про причины удаления неявного children написано здесь.
- Убран тип {} у ReactFragment. Это никогда не было правильным и в основном требовалось для взаимодействия с неявными пропом children.
- this.context по умолчанию станет unknown. Раньше по умолчанию был any. Это распространяется и на функцию useCallback, теперь, если тип не указан, то по умолчанию аргумент будет unknown.
- Удалены неиспользуемые типы. Приведены к общей терминологии, как они называются в репозитории и документации:
-StatelessComponent +FunctionComponent -SFC +FC -React.ReactType +React.ElementType -SFCElement +FunctionComponentElement // без замены -Props -RefForwardingComponent -SFCFactorygithub.com/Definit…ll/56210
* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates effects being destroyed on a mounted component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates effects being re-created on a mounted component.
* Layout effects are created
* Effect setup code runs
Для поддержки данной фичи необходимо, чтобы эффекты в компоненте были стабильны, т.е., например, созданный ресурс в эффекте удалялся в функции очистки.
С появлением подобного режима и при неправильной реализации useEffect могут появиться баги в приложении.
function useIsMounted() {
const isMountedRef = React.useRef(true);
React.useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
return () => isMountedRef.current;
}
Например, хук выше определяет, смонтирован ли компонент. В React 18 данный хук будет работать неправильно, т.к. при проверке стабильности эффектов React будет несколько раз запускать и очищать useEffect. Чтобы исправить хук, нужно добавить присвоение в самом эффекте:
React.useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);├───/apps │ ├───/routes │ │ ├───/posts // компоненты постов │ │ └───posts.jsx // это шаблонВ файле posts.jsx используется Remix компонент
<Outlet />, который вместо себя рендерит вложенные компоненты, как в react-router.
├───/apps │ ├───/routes │ | └───/users │ | ├───$userId.edit.jsx │ | ├───$userId.jsx │ │ └───$.jsxДля создания динамического роута необходимо начать имя файла со знака
$. Чтобы получить название параметра в компоненте нужно использовать хук: const { userId } = useParams().
Также можно определять дополнительные сегменты пути роута через точку. Теперь компонент $userId.edit.jsx будет открываться по пути /users/{{user-id}}/edit.
Если нужно создать роут, который бы открывался по любому пути, нужно создать файл с названием $.jsx. Тогда, если нет подходящего роута по данному URL, будет открываться $.jsx.
www.smashingmagazine.com/2022/03…ystifiedcreateListenerMiddleware для подписки на отправку экшенов или изменения стейта. Это API должно стать более простой альтернативой использования вместо более популярных saga или observable.
// Create the middleware instance and methods
const listenerMiddleware = createListenerMiddleware()
// Add one or more listener entries that look for specific actions.
// They may contain any sync or async logic, similar to thunks.
listenerMiddleware.startListening({
actionCreator: todoAdded,
effect: async (action, listenerApi) => {
// Run whatever additional side-effect-y logic you want here
console.log('Todo added: ', action.payload.text)
// Can cancel other running instances
listenerApi.cancelActiveListeners()
// Run async logic
const data = await fetchData()
// Pause until action dispatched or state changed
if (await listenerApi.condition(matchSomeAction)) {
// Use the listener API methods to dispatch, get state,
// unsubscribe the listener, start child tasks, and more
listenerApi.dispatch(todoAdded('Buy pet food'))
listenerApi.unsubscribe()
}
},
})
В своем блоге один из разработчиков Redux Toolkit, Марк Эриксон, рассказал об истории данного API и этапах его разработки. Разработка данной фичи была начата 2.5 года назад и потребовала много итераций, чтобы определить, какие варианты использования он должен охватывать, как должен выглядеть общедоступный API и как реализовать функциональность.
В основе у Redux есть несколько основных библиотек для работы с сайд эффектами:
- Thunks: отправить экшен, получить (dispatch, getState) в аргументах и выполнить любую логику внутри функции.
- Sagas: напишите функцию генератор, которая на экшен вызывает сайд эффект функцию.
- Observables: напишите RxJs пайплайн, который на экшен вызывает сайд эффект функцию.
По умолчанию в Redux Toolkit был выбран Thunks, как самый простой вариант работы с сайд эффектами. При разработке API createListenerMiddleware команда Redux Toolkit хотела отделить экшены и сайд эффекты, т.е. смотрела в сторону redux-saga. Однако по сложности пользовательского API была цель сделать его близким к redux-thunk.
blog.isquaredsoftware.com/2022/03…ddleware@rc тег:
npm install react@rc react-dom@rc // или yarn add react@rc react-dom@rcВ React 18 появится новое API для библиотек. Для поддержки конкурентного рендеринга сторонними библиотеками было добавлено следующее API: - useId – хук для генерации уникального id на клиенте и сервере, поддерживающего гидратацию. Подробнее. - useSyncExternalStore – хук для работы с внешними хранилищами, позволяющий производить параллельное чтение из источника и синхронную запись. Подробнее. - useInsertionEffect – хук для вставки стилей в процессе рендера, предназначен для CSS-in-JS библиотек. Подробнее. Новые изменения в строгом режиме (strict mode). В будущем React планирует поддерживать пользовательский UI, в котором части интерфейса можно будет удалить с сохранением стейта, чтобы потом их можно было восстановить. Например, переключение табов на странице, когда при смене таба нужный компонент будет отображаться сразу с восстановленным состоянием. Для поддержки такого UI сценария React добавляет новую проверку в development режиме в strict mode. Эта новая проверка будет автоматически демонтировать и повторно монтировать каждый компонент всякий раз, когда компонент монтируется в первый раз, восстанавливая предыдущее состояние при втором монтировании. Также в React 18 прекращается поддержка браузера Internet Explorer. reactjs.org/blog/20…ide.html
export async function getStaticProps() {
return {
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
Также возможно ручное обновление страницы. Например, такое пригодится для интернет-магазинов, при изменении цены товара на странице:
// pages/api/revalidate.js
export default async function handler(req, res) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
await res.unstable_revalidate('/path-to-revalidate')
return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return res.status(500).send('Error revalidating')
}
}
Другие изменения в новой версии:
- Улучшена поддержка SWC. Некоторые Babel плагины были портированы на Rust. Включена поддержка styled-components, relay.
- Автоматическая конфигурация Jest.
- Более быстрая минификация, используя SWC. В 7 раз быстрее чем Terser.
- Оптимизация изображений. API по работе с изображениями поддерживает паттерн ISR.
- Работа в сторону поддержки React 18, серверных компонентов и потокового SSR. Была обновлена документация для тех, кто хочет попробовать эти фичи.
https://nextjs.org/blog/next-12-1
import { makeStyles } from '@griffel/react';
const useClasses = makeStyles({
button: { color: 'red' },
icon: { paddingLeft: '5px' },
});
function Component() {
const classes = useClasses();
return (
<div>
<button className={classes.button} />
<span className={classes.icon} />
</div>
);
}
При написании стилей поддерживаются псевдоклассы, псевдоэлементы и at-правила. Также в Griffel есть API для создания глобальных стилей, которые не привязаны к компоненту – makeStaticStyles.
https://github.com/microsoft/griffel
Очень интересно наблюдать, как появляются новые CSS-in-JS библиотеки, особенно от таких крупных компаний. Еще в 2019 году Facebook рассказал о своей CSS-in-JS библиотеке – Stylex. Правда она еще до сих пор не появилась на гитхабе.