Привет, друзья!
В этой статье я хочу поговорить с вами об управлении состоянием и повторном рендеринге в React.js
.
Что такое состояние и зачем им управлять?
Состояние/state
можно определить как любые данные, которые влияют на рендеринг/rendering
компонентов. Состояние хранится в реактивных переменных/reactive variables
](в терминологии RxJS
).
Управление состоянием/state management
— это механизм, позволяющий создавать реактивные переменные, фиксировать изменения их значений и уведомлять об этом "заинтересованные" компоненты. Как правило, такой механизм реализуется с помощью паттерна Издатель-Подписчик/Publisher-Subscriber/Pub-Sub
.
Реакция компонентов на изменение состояния часто, но далеко не всегда завершается добавлением/модификацией/удалением HTML-элементов
в/из DOM
. Как известно, операции, связанные с манипулированием DOM
, являются очень медленными. Это обуславливает необходимость грамотного управления состоянием приложений.
Состояние бывает 2 видов:
-
локальное/local
: для определения такого состояние используется хукuseState
; -
распределенное/shared
: для создания такого состояния используется хукuseContext
(часто в сочетании с хукомuseReducer
) или библиотеки типаRedux
.
В свою очередь, распределенное состояние условно также можно разделить на 2 вида:
-
совместно размещенное/co-located
: такое состояние является общим для группы автономных, т.е. не находящихся в отношенияхпредок-потомок/parent-child
, компонентов (useContext/useReducer
; в качестве примера библиотеки, реализующей такой подход, можно назватьRecoil
); -
глобальное/global
— такое состояние является общим для всего приложения (Redux
).
Отличная иллюстрация того, чем следует руководствоваться при определении вида состояния:
Существует еще один подход к управлению состоянием, предназначенный для приложений, где большая часть состояния хранится на сервере. Такой подход предлагает, в частности, библиотека React Query
. Управление распределенным состоянием клиента в этом случае, как правило, реализуется за счет кеширования. Условно данный подход можно назвать смешанным/mixed
.
Чуть позже мы рассмотрим паттерн, позволяющий управлять состоянием приложения наиболее простым и эффективным способом.
Что такое рендеринг и почему он происходит?
Рендеринг — это вычисление компонентов (их структуры, дочерних компонентов, свойств и других данных), часто завершающаяся добавлением/модификацией/удалением HTML-элементов
в/из DOM
.
Выполнение кода функции (компонента) не всегда завершается отрисовкой/перерисовкой компонента в DOM
. Для определения необходимости в манипулировании DOM
React
использует множество эвристических техник: объект виртуальный DOM/virtual DOM
или, если быть точнее, волокно/fiber
, алгоритм согласование DOM/DOM diffing
(опять же, в настоящее время речь идет о согласовании волокон), атрибут ключ/key
и т.д.
Рендеринг бывает 2 видов:
-
первоначальный/initial
: происходит при инициализации приложения; -
повторный/rerendering
: происходит при определенных условиях (см. ниже).
Для управления первоначальным рендерингом предназначены такие вещи как утилита lazy
и компонент верхнего уровня Suspense
, позволяющие выполнять разделение кода/code splitting
, т.е. загружать (в этом React
помогает Webpack
) и выполнять только тот JS-код
, который используется приложением в данный момент, а также условный рендеринг/conditional rendering
компонентов, когда рендерятся только те компоненты, которые "соответствуют" текущему состоянию приложения.
Повторный рендеринг происходит в следующих случаях:
- изменение состояния компонента;
- изменение значений
пропов/props
, передаваемых компоненту; - повторный рендеринг родительского компонента.
Отличное визуальное руководство по повторному рендерингу в React
.
Потребление контекста/context consuming
также приводит к повторному рендерингу компонента. Это происходит при изменении передаваемых через контекст значений, которые используются (потребляются) компонентом. Поскольку чаще всего через контекст передается объект, это происходит почти всегда.
Для управления повторным рендерингом предназначены такие вещи как:
- хук
useCallback
, позволяющий запомнить (мемоизировать/memoize
) вычисление дорогостоящих с точки зрения производительности функций (обработчиков); - хук
useMemo
, который позволяет мемоизировать вычисление объектов (в том числе, передаваемых в качестве пропов другим компонентам) и вложенных элементов, таких как списки; - утилита
memo
, позволяющая мемоизировать компоненты за счетповерхностного/shallow
сравнения передаваемых им пропов.
Для мониторинга повторного рендеринга предназначены такие вещи как:
-
профилировщик (профайлер)/profiler
— поставляется в комплекте сReact Developer Tools
; - утилита
why-did-you-render
.
Чуть позже мы рассмотрим примеры использования этих инструментов.
Как сделать хорошо?
Разработаем простое React/TypeScript-приложение
и научимся управлять его состоянием, а также контролировать рендеринг его компонентов.
Создаем шаблон приложения с помощью create-react-app
:
yarn create react-app my-app --template typescript
# or
npx create-react-app ...
Наше приложение будет состоять из 2 компонентов: RandomString
и RandomHexColor
. Каждый из этих компонентов будет состоять из 3 компонентов: Container
, Button
и компонент, объединяющий их в единое целое.
Нам потребуется утилита для создания хранилища состояния (createStore
), а также хук для вывода в консоль сообщений о рендеринге компонентов, начиная со второго рендеринга (useLogAfterFirstRender
).
Структура директории src
будет следующей:
- components
- RandomHexColor
- Button.tsx
- Container.tsx
- index.tsx
- RandomString
- Button.tsx
- Container.tsx
- index.tsx
- index.ts
- hooks
- useLogAfterFirstRender.ts
- types
- index.d.ts
- utils
- createStore.tsx
- App.scss
- App.tsx
- index.tsx
Состояние будет общим (распределенным) для компонентов RandomString
и RandomHexColor
. Имеет смысл начать с разработки утилиты для создания хранилища.
Вот чем я руководствовался при реализации данной утилиты:
- утилита должна принимать объект (
хранилище/store
) с состоянием (state
) и сеттерами (setters
) — методами для изменениячастей состояния/state slices
или всего состояния; - сеттеры должны мемоизироваться во избежание повторного вычисления, но при этом всегда иметь доступ к актуальному состоянию за счет декорирования;
- провайдеры состояния и сеттеров должны быть автономными, чтобы повторное вычисление состояния не приводило к повторному рендерингу компонентов, потребляющих сеттеры;
- утилита должна возвращать провайдер и хуки для извлечения состояния и сеттеров.
Начнем с определения типов для утилиты (types/index.d.ts
) и дочерних компонентов:
// тип для состояния, передаваемого утилите
export type State = { [k: string]: any }
// тип для начальных сеттеров
export type InitialSetters = {
[k: string]: (s: State, ...args: any[]) => void | Partial<State>
}
// тип для проксированных сеттеров
export type ProxySetters = { [k: string]: (v: any) => void }
// тип для хранилища
export type Store = { state: State; setters: InitialSetters }
// тип для дочерних компонентов
export type Children = { children: React.ReactNode }
Сигнатура объекта хранилища, передаваемого утилите:
const store = {
state: {
stateSlice: value
},
setters: {
setter: (state, args) => stateSlice
}
}
Приступаем к реализации утилиты.
Импортируем хуки и типы:
import React, { createContext, useContext, useMemo, useState } from 'react'
import { State, InitialSetters, ProxySetters, Store, Children } from 'types'
Определяем утилиту для проксирования сеттеров:
const createSetters = (
setters: InitialSetters,
setState: React.Dispatch<(prevState: State) => State>
) => {
const _setters = {} as ProxySetters
for (const key in setters) {
_setters[key] = (...args) => {
setState((state) => {
const newState = setters[key](state, ...args)
return { ...state, ...newState }
})
}
}
return _setters
}
Данная утилита принимает сеттеры и декорирует их таким образом, что они, во-первых, получают состояние в качестве первого аргумента, во-вторых, обновляют состояние, возвращая объект, ключи которого должны совпадать с ключами модифицируемых частей состояния. Сеттер также может вернуть модифицированное состояние в целом.
Определяем утилиту для создания хранилища:
export function createStore(store: Store) {
// разделяем контексты и провайдеры (ниже)
const StateContext = createContext<State>(store.state)
const SetterContext = createContext<ProxySetters>(store.setters)
const Provider = ({ children }: Children) => {
const [state, setState] = useState(store.state)
// это позволит избежать повторного рендеринга компонентов, потребляющих сеттеры (кнопок)
const setters = useMemo(() => createSetters(store.setters, setState), [])
return (
<StateContext.Provider value={state}>
<SetterContext.Provider value={setters}>
{children}
</SetterContext.Provider>
</StateContext.Provider>
)
}
const useStore = () => useContext(StateContext)
const useSetter = () => useContext(SetterContext)
return [Provider, useStore, useSetter] as const
}
Используем данную утилиту для создания хранилища в App.tsx
.
Импортируем стили, тип, утилиту и компоненты:
import './App.scss'
import { Store } from 'types'
import { createStore } from 'utils/createStore'
import { RandomString, RandomHexColor } from 'components'
Создаем хранилище:
const store: Store = {
state: {
randomString: '',
randomHexColor: ''
},
setters: {
setRandomString: (_, randomString) => ({ randomString }),
setRandomHexColor: (_, randomHexColor) => ({ randomHexColor })
}
}
Наше хранилище содержит 2 реактивные переменные randomString
и randomHexColor
и 2 метода для изменения значений этих переменных: setRandomString
и setRandomHexColor
, соответственно.
Создаем хранилище, экспортируем хуки и оборачиваем компоненты в провайдер:
// поскольку утилита возвращает массив,
// мы вольны именовать провайдер и хуки как угодно,
// что способствует совместному размещению состояний,
// позволяя избежать путаницы в провайдерах/хуках
export const [Provider, useStore, useSetter] = createStore(store)
function App() {
return (
<Provider>
<RandomString />
<RandomHexColor />
</Provider>
)
}
export default App
// шрифт
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;400;600&display=swap');
// переменные
$primary: #0275d8;
$success: #5cb85c;
$warning: #f0ad4e;
$light: #f7f7f7;
$dark: #292b2c;
// миксин для сброса стилей
@mixin reset($font-family, $font-size, $color) {
margin: 0;
padding: 0;
box-sizing: border-box;
@if $font-family {
font-family: $font-family;
}
@if $font-size {
font-size: $font-size;
}
@if $color {
color: $color;
}
}
// миксин для центрирования
@mixin flex-center($column: false) {
display: flex;
justify-content: center;
align-items: center;
@if $column {
& {
flex-direction: column;
}
}
}
*,
*::before,
*::after {
// применяем миксин
@include reset('Montserrat', 1rem, $dark);
}
#root {
min-height: 100vh;
@include flex-center(true);
// свойство gap не работает в Firefox, поэтому приходится делать так
div + div {
margin-top: 1rem;
}
.random-string,
.random-color {
@include flex-center(true);
}
.random-color {
div {
width: 120px;
height: 120px;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
}
button {
margin: 0.5rem;
padding: 0.5rem 1rem;
border: none;
outline: none;
border-radius: 4px;
background-color: $primary;
color: $light;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
cursor: pointer;
user-select: none;
&:active {
box-shadow: none;
}
}
.color-button {
background-color: $success;
}
.reload-button {
background-color: $warning;
color: $dark;
}
}
Не забываем установить sass
: yarn add -D sass
или npm i -D sass
.
Хук для вывода в консоль сообщений о рендеринге (hooks/useLogAfterFirstRender.ts
):
import { useEffect, useRef } from 'react'
export const useLogAfterFirstRender = (message: string) => {
const firstRender = useRef(true)
useEffect(() => {
firstRender.current = false
}, [])
if (!firstRender.current) {
console.log(message)
}
}
Теперь займемся компонентами.
Контейнер для случайной строки (RandomString/Container.tsx
):
import { useStore } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
export const Container = () => {
// извлекаем значение переменной `randomString`
const { randomString } = useStore()
useLogAfterFirstRender('Random string container')
return <p>{randomString || 'qwerty'}</p>
}
Кнопка для установки значения строки (RandomString/Button.tsx
):
import { useCallback } from 'react'
import { useSetter } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
// функция для генерации случайной строки
const getRandomString = () => Math.random().toString(36).slice(2)
export const Button = () => {
// извлекаем сеттер для изменения значения переменной `randomString`
const { setRandomString } = useSetter()
useLogAfterFirstRender('Random string button')
// мемоизируем обработчик нажатия кнопки
const onClick = useCallback(() => {
const randomString = getRandomString()
// используем сеттер
setRandomString(randomString)
}, [setRandomString])
return <button onClick={onClick}>Set random string</button>
}
Компонент RandomString
(RandomString/index.tsx
):
import { Container } from './Container'
import { Button } from './Button'
export const RandomString = () => (
<div className='random-string'>
<Container />
<Button />
</div>
)
Компонент RandomHexColor
:
// RandomHexColor/Container.tsx
import { useStore } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
export const Container = () => {
// извлекаем значение переменной `randomHexColor`
const { randomHexColor } = useStore()
useLogAfterFirstRender('Random HEX color container')
// мемоизируем вычисление стилей
const containerStyles = useMemo(
() => ({
backgroundColor: randomHexColor || 'deepskyblue'
}),
[randomHexColor]
)
return <div style={containerStyles}></div>
}
// RandomHexColor/Button.tsx
import { useCallback } from 'react'
import { useSetter } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
// функция для генерации случайного цвета
const getRandomHexColor = () =>
`#${((Math.random() * 0xffffff) << 0).toString(16)}`
export const Button = () => {
// извлекаем сеттер для изменения значения переменной `randomHexColor`
const { setRandomHexColor } = useSetter()
useLogAfterFirstRender('Random HEX color button')
const onClick = useCallback(() => {
const randomHexColor = getRandomHexColor()
// используем сеттер
setRandomHexColor(randomHexColor)
}, [setRandomHexColor])
return (
<button onClick={onClick} className='color-button'>Set random HEX color</button>
)
}
Выполняем повторный экспорт компонентов (components/index.ts
):
export { RandomString } from './RandomString'
export { RandomHexColor } from './RandomHexColor'
Посмотрим, как работает наше приложение. Выполняем команду yarn start
или npm start
:
Нажимаем на кнопку Set random string
:
Ожидаемо меняется значение строки.
Но что это? В консоли мы видим сообщения о повторном рендеринге как компонента RandomString
, так и компонента RandomHexColor
. Но мы же ничего не делали с RandomHexColor
. Почему же он подвергся повторному рендерингу?
Обратите внимание:
- повторный рендеринг не означает перерисовки компонента
Random HEX color container
, а лишь его повторное вычисление; - кнопки повторно не рендерятся. Этим мы обязаны мемоизации сеттеров, выполненной при создании хранилища.
Обратимся за помощью к профилировщику для того, чтобы понять, почему происходит повторный рендеринг, казалось бы, незаинтересованного компонента.
Определим отображаемые названия компонентов:
Container.displayName = 'Random string container'
// и т.д.
Переходим во вкладку Profiler
инструментов разработчика в браузере.
Настраиваем его: нажимаем на иконку шестеренки View settings
и выбираем Highlight updates when components render.
на вкладке General
и Record why each component rendered while profiling.
на вкладке Profiler
:
Закрываем панель настроек профилировщика и нажимаем на стрелку Reload and start profiling
в левом верхнем углу рядом с иконкой записи:
Нажимаем на кнопку Set random string
, видим подсветку повторного рендеринга.
Нажимаем на иконку записи для ее остановки и переключаем представление на Ranked chart
:
В правом верхнем углу мы видим текущий рендеринг и общее количество рендерингов (1 / 2
). Сейчас мы наблюдаем результаты первоначального рендеринга, о чем говорит сообщение при наведении курсора на любой компонент:
Переключаемся на результаты второго рендеринга и наводим курсор на компонент Random HEX color container
:
Сообщение говорит нам о том, что повторный рендеринг данного компонента произошел по причине повторного рендеринга его родительского компонента, т.е. провайдера контекста (Context.Provider
) (его мы видим ниже). Это верно, но лишь отчасти.
Устанавливаем пакет why-did-you-render
:
yarn add -D @welldone-software/why-did-you-render
# or
npm i ...
Подключаем его в src/index.tsx
для режима разработки:
if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render')
whyDidYouRender(React, {
titleColor: 'green',
diffNameColor: 'blue',
logOnDifferentValues: true,
trackHooks: true
})
}
Обратите внимание: после перехода CRA
на react-scripts v4
(где используется новый способ трансформации JSX в JS) данная утилита и ряд других какое-то время не работали. Если у вас возникнут проблемы, связанные с тем, что wdyr
не выводит сообщения в консоль, поищите ответ в этом обсуждении.
Регистрируем компонент RandomHexColor/Container
:
Container.whyDidYouRender = true
На всякий случай перезапускаем сервер для разработки.
Снова нажимаем на кнопку Set random string
и получаем гораздо более информативное сообщение:
Сначала мы видим, что значение переменной randomString
изменилось с ""
на "wpkhijfo7xi"
, затем, что значение, передаваемое через контекст, изменилось с { randomString: '', randomHexColor: '' }
на { randomString: 'wpkhijfo7xi', randomHexColor: '' }
.
Так вот в чем дело! При изменении значения любой реактивной переменной генерируется новый объект.
Внимание: в подавляющем большинстве случаев, при условии правильного размещения распределенных состояний, это не будет проблемой для производительности приложения.
Однако предположим, что повторное вычисление компонента Random HEX color container
крайне нежелательно. Допустим, мы хотим, чтобы этот компонент подвергался повторному рендерингу только при изменении значения переменной randomHexColor
. Как нам этого добиться? Существует один способ, но он имеет некоторые побочные эффекты.
Данный способ заключается в мемоизации компонента с помощью memo
. Но для того, чтобы у нас появились пропы для сравнения, состояние необходимо поднять/lift
до ближайшего родительского компонента. В нашем случае таким компонентом является RandomHexColor/index.tsx
. Перепишем его следующим образом:
import { useStore } from 'App'
import { Button } from './Button'
import { Container } from './Container'
export const RandomHexColor = () => {
const { randomHexColor } = useStore()
return (
<div className='random-color'>
{/* передаем переменную `randomHexColor` как проп */}
<Container randomHexColor={randomHexColor} />
<Button />
</div>
)
}
Отрефакторим компонент RandomHexColor/Container.tsx
:
import { memo } from 'react'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
type Props = {
randomHexColor: string
}
// мемоизируем компонент
export const Container = memo(({ randomHexColor }: Props) => {
useLogAfterFirstRender('Random HEX color container')
const containerStyles = useMemo(
() => ({
backgroundColor: randomHexColor || 'deepskyblue'
}),
[randomHexColor]
)
return <div style={containerStyles}></div>
})
Возвращаемся в браузер и нажимаем Set random string
:
Компонент Random HEX color container
больше не рендерится при изменении значения переменной randomString
, но за все приходится платить: теперь повторно рендерится компонент Random HEX color button
, поскольку повторному рендерингу подвергается его предок (RandomHexColor
). Другими словами, наша оптимизация нивелировала выгоду от предварительной мемоизации сеттеров.
Мемоизируем компонент Random HEX color button
с помощью memo
:
import { memo, useCallback, useState } from 'react'
import { useSetter } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
// мемоизируем компонент
export const Button = memo(() => {
const { setRandomHexColor } = useSetter()
useLogAfterFirstRender('Random HEX color button')
const onClick = useCallback(() => {
const randomHexColor = getRandomHexColor()
setRandomHexColor(randomHexColor)
}, [setRandomHexColor])
return (
<button onClick={onClick} className='color-button'>
Set random HEX color
</button>
)
})
Снова нажимаем Set random string
:
Теперь при изменении randomString
повторно рендерится только Random string container
. Мы добились, чего хотели, но еще раз повторю: наши оптимизации являются преждевременными, в них не было совершенно никакой необходимости.
Последний вопрос: что если нам потребуется выполнить принудительный повторный рендеринг компонента, например, Random HEX color button
. Эта задача решается за счет обновления локального состояния. Перепишем данный компонент следующим образом:
import { memo, useCallback, useState } from 'react'
import { useSetter } from 'App'
import { useLogAfterFirstRender } from 'hooks/useLogAfterFirstRender'
const getRandomHexColor = () =>
`#${((Math.random() * 0xffffff) << 0).toString(16)}`
export const Button = memo(() => {
const { setRandomHexColor } = useSetter()
// создаем локальное состояние
const [, setState] = useState({})
const forceUpdate = useCallback(() => {
// обновление состояния повлечет за собой повторный рендеринг
setState({})
}, [])
useLogAfterFirstRender('Random HEX color button')
const onClick = useCallback(() => {
const randomHexColor = getRandomHexColor()
setRandomHexColor(randomHexColor)
}, [setRandomHexColor])
return (
<>
<button onClick={onClick} className='color-button'>
Set random HEX color
</button>
{/* добавляем кнопку для принудительного рендеринга */}
<button onClick={forceUpdate} className='reload-button'>
Force update
</button>
</>
)
})
Нажимаем Force update
:
Получаем сообщение о рендеринге Random HEX color button
.
Пожалуй, это все, чем я хотел поделиться с вами в этой статье.
Надеюсь, вы узнали что-то новое и не зря потратили время.
Благодарю за внимание и happy coding!
Комментарии (18)
markelov69
22.12.2021 14:24Мда… Очередная лютая дичь, всё что угодно готовы выдумывать лишь бы очередную вредную статейку на хабре опубликовать, из разряда как делать категорически нельзя, вместо того чтобы просто взять MobX в связку к React и забыть о головной боли навсегда.
rudinandrey
22.12.2021 14:30+5да, да, давайте перестанем изобретать велосипеды, ведь все уже изобретено. Давайте будет затыкать рты тем кто пытается что-то изобретать. Ведь ничего лучше никогда чем Redux или MOBX уже изобрести нельзя. Все паттерны открыты, все архитектуры уже созданы. Все, изучай и делай и не изобретай велосипедов.
nin-jin
22.12.2021 16:09Изобретать велосипед имеет смысл, лишь когда у тебя есть либо радикально новая идея, либо идеи по улучшению существующих реализаций, но нет возможноости их улучшить. В данном же случае мы видим скорее попытку прикрутить к велосипеду с квадратными колёсами пружинки на ободе, чтобы не так сильно стучало по мозгам.
alexshipin
22.12.2021 19:59иногда проще заново изобрести велосипед (типа "Школьник", "Аист" и тд), который будет работать в необходимых для того условиях (пару раз в десять лет выехать на нём), нежели брать монструозный спортивный велосипед с десятком скоростей за 100500 $.
Так и тут, локальное решение, умещенное в пределы данной части проекта, оправдает себя лучше, нежели тащить весь Redux/Mobx в целый проектnin-jin
22.12.2021 20:28-1Либо можно взять микромодульный фреймворк, с которым не приходится платить за то, что не используешь.
markelov69
22.12.2021 23:40+1Так и тут, локальное решение, умещенное в пределы данной части проекта, оправдает себя лучше, нежели тащить весь Redux/Mobx в целый проект
Что за глупости? Тащить? Вы что тащите прицеп 10 тонный который замедляет время загрузки на 5-10 секунд и к размеру бандла 5-10mb добавляет?
markelov69
22.12.2021 18:12+2Дело не в этом, велосипеды это хорошо, но при условии что они реально лучше/удобнее того, что есть сейчас. А реальность такова, в JS есть getters/setters и Proxy и решение построенное на них — является безальтернативно лучшим, всё остальное не использует это, поэтому сразу же очень сильно проигрывает. Т.е. если решение для стейт менеджера не использует getters/setters — то оно автоматически провальное и кривое. Поэтому MobX сейчас единственное и безальтернативное решения для React т.к. построено на основе getters/setters и Proxy.
Showvars
23.12.2021 02:20+1Считаю, что любой велосипед — это хорошо. Не важно, лучше он или нет. Автор потратил свое время на изобретение. Возможно он просто повторил чужой опыт, возможно он экспериментировал с какой то навязчивой идеей. В любом случае он получил полезный опыт. Даже если это полностью провальный проект. Более того, выложив свое детище где то в публичный доступ, выставив на суд людской, он получает актуальную критику — что тоже бесценный опыт. Глядишь, так на n-ый раз, у m-ого «фреймворкостроителя» получится что то действительно грандиозное.
markelov69
23.12.2021 10:34Проблема в том, что такие "статьи" провоцируют некоторые типы людей прислушиваться к их мнению и рождается от этого куча проблем в коммерческих проектах, люди начинают там использовать голый реакт и все "решать" с помощью контекста и т.д и т.п. Или вообще максимально до смешного доходит что Redux используют до сих пор.
Showvars
23.12.2021 14:39такие "статьи" провоцируют некоторые типы людей прислушиваться к их мнению и рождается от этого куча проблем в коммерческих проектах
Это не наша проблема и не проблема автора. С данной статьей я также категорически не согласен, но лучше писать статью, чем не писать. У автора есть желание и время, у нас с вами — комментарии и иногда голосование.
noodles
25.12.2021 13:06React.js: размышления об управлении состоянием и повторном рендеринге
Что о нём размышлять? Уже ж вроде давно определился лидер и всё "починилось". Возьмите react+mobx+typescript+ui-kit-по-вкусу и просто делайте свою работу.
Освободившееся время и энергию лучше вкладывать в более фундаментальные вещи, типа - сети, алгоритмы, книжки, здоровье, навыки переговоров\убеждения, личную жизнь..)
Alexandroppolus
999-я по счету попытка использовать контекст как стейт-манагер... Эта идея не покидает умы, хотя давно опровергнута.
По сути одно и то же. В обоих случаях данные лежат в контексте.