useConst — вспомогательный хук библиотеки Chakra
UI, предназначенный для создания неизменяемого
значения, вычисляемого только один раз за жизненный цикл
компонента. Хук гарантирует, что значение будет
инициализировано при первом рендере и не изменится при
последующих перерендерах, даже если компонент обновляется.
Такой подход полезен при работе с:
useConst можно рассматривать как упрощённую и
более безопасную альтернативу useMemo без
зависимостей.
import { useConst } from "@chakra-ui/react"
const value = useConst(initialValue)
или
const value = useConst(() => computeValue())
| Параметр | Тип | Описание |
|---|---|---|
initialValue |
any |
Значение или функция, возвращающая значение |
Если передается функция, она выполняется только один раз.
useConst использует useRef внутри своей
реализации. Значение сохраняется в рефе и не пересчитывается при
повторных рендерах.
Упрощённая логика работы:
function useConst(init) {
const ref = React.useRef()
if (ref.current === undefined) {
ref.current = typeof init === "function" ? init() : init
}
return ref.current
}
Ключевая идея:
function Example() {
const randomId = useConst(() => Math.random())
return <div>{randomId}</div>
}
При каждом рендере компонента значение randomId остается
неизменным.
Без useConst:
function Example() {
const randomId = Math.random()
return <div>{randomId}</div>
}
Теперь значение будет меняться при каждом обновлении.
Распространённый сценарий — создание конфигурационного объекта.
function Chart() {
const config = useConst({
width: 400,
height: 300,
theme: "dark"
})
return <ChartComponent config={config} />
}
Объект создается один раз и не пересоздается при каждом рендере, что уменьшает вероятность лишних перерисовок дочерних компонентов.
Если вычисление значения затратное, используется функция:
function Example() {
const data = useConst(() => {
return generateLargeDataset()
})
return <DataViewer data={data} />
}
generateLargeDataset() выполняется только один
раз.
useConst удобен для генерации стабильных
идентификаторов.
function InputField() {
const id = useConst(() => `input-${Math.random().toString(36).slice(2)}`)
return (
<>
<label htmlFor={id}>Name</label>
<input id={id} />
</>
)
}
Идентификатор остается постоянным независимо от количества перерисовок.
Иногда компоненту требуется один постоянный экземпляр объекта.
function WebSocketComponent() {
const socket = useConst(() => new WebSocket("wss://example.com"))
return <Connection socket={socket} />
}
Без useConst новый WebSocket создавался бы при каждом
рендере.
Массивы часто используются как константные структуры данных.
function Menu() {
const items = useConst([
{ id: 1, label: "Home" },
{ id: 2, label: "Profile" },
{ id: 3, label: "Settings" }
])
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.label}</li>
))}
</ul>
)
}
Массив не будет пересоздаваться при обновлениях состояния.
useMemo| Хук | Когда используется | Пересчет |
|---|---|---|
useMemo |
мемоизация вычислений | при изменении зависимостей |
useConst |
значение должно существовать один раз | никогда |
Пример useMemo:
const value = useMemo(() => compute(), [deps])
Пример useConst:
const value = useConst(() => compute())
Разница:
useMemo зависит от массива зависимостейuseConst игнорирует любые
зависимостиuseRefuseConst фактически является удобной оберткой над
useRef.
useReffunction Example() {
const ref = useRef(null)
if (ref.current === null) {
ref.current = createValue()
}
return ref.current
}
useConstfunction Example() {
const value = useConst(() => createValue())
return value
}
useConst делает код:
Если объект создается внутри компонента, React будет считать его новым при каждом рендере.
Проблемный код:
function Parent() {
const config = {
animation: true
}
return <Child config={config} />
}
Компонент Child будет получать новую
ссылку на объект каждый раз.
Решение:
function Parent() {
const config = useConst({
animation: true
})
return <Child config={config} />
}
Теперь ссылка стабильна.
useConst может хранить функцию.
function Component() {
const logger = useConst(() => {
return message => console.log(message)
})
logger("Hello")
}
Функция создается только один раз.
Иногда значение требует много шагов для создания.
function Editor() {
const editor = useConst(() => {
const instance = new EditorEngine()
instance.enableHistory()
instance.setTheme("dark")
return instance
})
return <EditorView editor={editor} />
}
Инициализация выполняется только при первом рендере.
useConstНаиболее типичные сценарии:
const options = useConst({
animation: true,
duration: 300
})
const dataset = useConst(() => buildDataset())
const store = useConst(() => new Store())
const uuid = useConst(generateUUID)
const defaultState = useConst({
loading: false,
error: null
})
После инициализации изменить значение невозможно.
const value = useConst(10)
value всегда будет равен 10.
Если значение должно зависеть от пропсов или состояния, следует
использовать useMemo.
Неправильный пример:
const value = useConst(() => props.count * 2)
props.count может изменяться, но значение пересчитано не
будет.
Правильный вариант:
const value = useMemo(() => props.count * 2, [props.count])
useConst не заменяет useState.
const value = useConst(10)
Это неизменяемая константа, а не состояние.
const data = useConst(() => expensiveComputation())
const handlers = useConst({
onOpen: () => setOpen(true),
onClose: () => setOpen(false)
})
const chart = useConst(() => new ChartJS(ctx, config))
const constants = useConst({
maxItems: 20,
pageSize: 10
})
В Chakra UI useConst применяется для:
Хук активно используется внутри сложных компонентов библиотеки, где важно избежать лишних вычислений и повторных инициализаций.
Если значение должно:
| Требование | Решение |
|---|---|
| изменяться | useState |
| пересчитываться при изменении зависимостей | useMemo |
| существовать один раз | useConst |
| хранить изменяемое значение без рендера | useRef |
import { useConst, Box, Button } from "@chakra-ui/react"
function Counter() {
const initialData = useConst(() => ({
start: 10,
step: 2
}))
const [count, setCount] = React.useState(initialData.start)
return (
<Box>
<Box>{count}</Box>
<Button onCl ick={() => setCount(c => c + initialData.step)}>
Increment
</Button>
</Box>
)
}
initialData создается только один раз и используется на
протяжении всего жизненного цикла компонента.