- Нашр шудааст дар
Салом React, Салом useEffect (Ин шаа Аллоҳ)
- Муаллифон
- Ном
- Imamuzzaki Abu Salam
- https://x.com/ImBIOS_Dev
Дар ин мақола, ман ба шумо нишон медиҳам, ки чӣ тавр React-ро барои иваз кардани useEffect дар аксари ҳолатҳо истифода бурдан мумкин аст.
Ман "Худо ҳафиз, useEffect" аз ҷониби Дэвид Хоуршид-ро тамошо мекардам ва ин 🤯 ҷолиби диққат аст ва ба таври хуб 😀 маро ба ҳайрат меорад. Ман розӣ ҳастам, ки useEffect ин қадар истифода шудааст, ки рамзи моро ифлос ва нигоҳдорӣ карданаш мушкил мекунад. Ман муддати тӯлонӣ useEffect-ро истифода мебурдам ва ман аз истифодаи нодурусти он гунаҳкорам. Ман боварӣ дорам, ки React хусусиятҳое дорад, ки рамзи маро тозатар ва нигоҳдории онро осонтар мекунад.
useEffect чист?
useEffect як хуки аст, ки ба мо имкон медиҳад, ки таъсири тарафҳоро дар компоненти функсия иҷро кунем. Он componentDidMount, componentDidUpdate ва componentWillUnmount-ро дар як API ягона муттаҳид мекунад. Ин як хуки ҷолиби диққат аст, ки ба мо имкон медиҳад, ки чизҳои зиёд анҷом диҳем. Аммо ин инчунин як хуки хеле хатарнок аст, ки метавонад боиси хатогиҳои зиёд шавад.
Чаро useEffect хатарнок аст?
Биёед ба мисоли зерин назар андозем:
import React, { useEffect } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setCount((c) => c + 1)
}, 1000)
return () => clearInterval(interval)
}, [])
return <div>{count}</div>
}
Ин як ҳисобкунаки оддӣ аст, ки ҳар сония зиёд мешавад. Он useEffect-ро барои муқаррар кардани интервал истифода мебарад. Он инчунин useEffect-ро барои тоза кардани интервал ҳангоми нобуд шудани компонент истифода мебарад. Параграфи коди дар боло як истифодаи маъмулии useEffect аст. Ин як мисоли оддӣ аст, аммо инчунин як мисоли бад аст.
Мушкили ин мисол дар он аст, ки интервал ҳар дафъае, ки компонент аз нав рендер мешавад, муқаррар карда мешавад. Агар компонент барои ҳама гуна сабаб аз нав рендер шавад, интервал аз нав муқаррар карда мешавад. Интервал ду маротиба дар як сония даъват карда мешавад. Ин дар ин мисоли оддӣ мушкил нест, аммо ҳангоми мураккабтар шудани интервал метавонад мушкили бузург бошад. Ин инчунин метавонад боиси нуқсонҳои хотира шавад.
Чӣ тавр инро ислоҳ кардан мумкин аст?
Роҳҳои зиёде барои ҳалли ин мушкил вуҷуд дорад. Як роҳ истифодаи useRef барои нигоҳдории интервал аст.
import React, { useEffect, useRef } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
const intervalRef = useRef()
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount((c) => c + 1)
}, 1000)
return () => clearInterval(intervalRef.current)
}, [])
return <div>{count}</div>
}
Коди дар боло хеле беҳтар аз мисоли қаблӣ аст. Он ҳар дафъае, ки компонент аз нав рендер мешавад, интервалро муқаррар намекунад. Аммо он то ҳол ба такмил ниёз дорад. Он то ҳол каме мураккаб аст. Ва он то ҳол useEffect-ро истифода мебарад, ки як хуки хеле хатарнок аст.
useEffect барои таъсирҳо нест
Чӣ тавре ки мо дар бораи useEffect медонем, он componentDidMount, componentDidUpdate ва componentWillUnmount-ро дар як API ягона муттаҳид мекунад. Биёед баъзе мисолҳои онро диҳем:
useEffect(() => {
// componentDidMount?
}, [])
useEffect(() => {
// componentDidUpdate?
}, [something, anotherThing])
useEffect(() => {
return () => {
// componentWillUnmount?
}
}, [])
Фаҳмидани он осон аст. useEffect барои иҷрои таъсири тарафҳо ҳангоми насб шудани компонент, навсозӣ ва нобуд шудан истифода мешавад. Аммо он танҳо барои иҷрои таъсири тарафҳо истифода намешавад. Он инчунин барои иҷрои таъсири тарафҳо ҳангоми аз нав рендер шудани компонент истифода мешавад. Иҷрои таъсири тарафҳо ҳангоми аз нав рендер шудани компонент идеяи хубе нест. Ин метавонад боиси хатогиҳои зиёд шавад. Беҳтар аст, ки хукҳои дигарро барои иҷрои таъсири тарафҳо ҳангоми аз нав рендер шудани компонент истифода барем.
import React, { useState, useEffect } from 'react'
const Example = () => {
const [value, setValue] = useState('')
const [count, setCount] = useState(-1)
useEffect(() => {
setCount(count + 1)
})
const onChange = ({ target }) => setValue(target.value)
return (
<div>
<input type="text" value={value} onChange={onChange} />
<div>Шумораи тағиротҳо: {count}</div>
</div>
)
}
useEffect муқаррар кардани ҳолат нест
import React, { useState, useEffect } from 'react'
const Example = () => {
const [count, setCount] = useState(0)
// Шабеҳ ба componentDidMount ва componentDidUpdate:
useEffect(() => {
// Барои навсозии унвони ҳуҷҷат бо истифода аз API браузер
document.title = `Шумо ${count} маротиба клик кардед`
}) // <-- ин мушкил аст, 😱 маҷмӯи вобастагӣ гум шудааст
return (
<div>
<p>Шумо ${count} маротиба клик кардед</p>
<button onClick={() => setCount(count + 1)}>Маро клик кунед</button>
</div>
)
}
Ман тавсия медиҳам, ки ин ҳуҷҷатгузориро хонед: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
Императивӣ нисбат ба Эъломи
Императивӣ: Вақте ки чизе рух медиҳад, ин таъсирро иҷро кунед.
Эъломи: Вақте ки чизе рух медиҳад, он боиси тағирёбии ҳолат мешавад ва вобаста ба (маҷмӯи вобастагӣ), ки кадом қисмҳои ҳолат тағир ёфтаанд, ин таъсир бояд иҷро шавад, аммо танҳо агар шароит муайян бошад. Ва React метавонад онро боз барои ҳеҷ сабаб рендери ҳамзамон иҷро кунад.
Мафҳум нисбат ба Татбиқ
Мафҳум:
useEffect(() => {
doSomething()
return () => cleanup()
}, [whenThisChanges])
Татбиқ:
useEffect(() => {
if (foo && bar && (baz || quo)) {
doSomething()
} else {
doSomethingElse()
}
// эҳ, ман тозакуниро фаромӯш кардам
}, [foo, bar, baz, quo])
Татбиқи воқеии ҷаҳон:
useEffect(() => {
if (isOpen && component && containerElRef.current) {
if (React.isValidElement(component)) {
ionContext.addOverlay(overlayId, component, containerElRef.current!);
} else {
const element = createElement(component as React.ComponentClass, componentProps);
ionContext.addOverlay(overlayId, element, containerElRef.current!);
}
}
}, [component, containerElRef.current, isOpen, componentProps]);
useEffect(() => {
if (removingValue && !hasValue && cssDisplayFlex) {
setCssDisplayFlex(false)
}
setRemovingValue(false)
}, [removingValue, hasValue, cssDisplayFlex])
Навиштани ин код даҳшатнок аст. Ғайр аз ин, он дар пойгоҳи кодии мо муқаррарӣ ва бетартиб мешавад. 😱🤮
Таъсирҳо куҷо мераванд?
React 18 ду маротиба дар лаҳзаи насб (дар режими қатъӣ) таъсир мерасонад. Насб/таъсир (╯°□°)╯︵ ┻━┻ -> Нобуд кардан (шаклшуда)/тозакунӣ ┬─┬ /( º _ º /) -> Насби аз нав/таъсир (╯°□°)╯︵ ┻━┻
Оё он бояд дар беруни компонент ҷойгир карда шавад? useEffect-и пешфарз? Ум... ногувор. Ҳм... 🤔 Мо наметавонем онро ба рендер ҷойгир кунем, зеро он бояд ҳеҷ гуна таъсири тарафҳо дар он ҷо набошад, зеро рендер танҳо мисли дасти рост дар тенси математикӣ аст. Он бояд танҳо натиҷаи ҳисобкунӣ бошад.
useEffect барои чӣ аст?
Синхронизатсия
useEffect(() => {
const sub = createThing(input).subscribe((value) => {
// чизеро бо арзиш анҷом диҳед
})
return sub.unsubscribe
}, [input])
useEffect(() => {
const handler = (event) => {
setPointer({ x: event.clientX, y: event.clientY })
}
elRef.current.addEventListener('pointermove', handler)
return () => {
elRef.current.removeEventListener('pointermove', handler)
}
}, [])
Таъсири амал нисбат ба Таъсири фаъолият
Оташзанӣ ва фаромӯш кардан Синхронизатсияшуда
(Таъсири амал) (Таъсири фаъолият)
0 ---------------------- ----------------- - - -
o o | A | o o | A | A
o o | | | o o | | | |
o o | | | o o | | | |
o o | | | o o | | | |
o o | | | o o | | | |
o o | | | o o | | | |
o o V | V o o V | V |
o-------------------------------------------------------------------------------->
Нобуд кардан Насби аз нав
Таъсири амал куҷо меравад?
Идоракунандагони ҳодиса. Кӯтоҳ.
<form
onSubmit={(event) => {
// 💥 таъсири тарафҳо!
submitData(event)
}}
>
{/* ... */}
</form>
Маълумоти хубе дар Beta React.js мавҷуд аст. Ман тавсия медиҳам, ки онро хонед. Махсусан "Оё идоракунандагони ҳодиса метавонанд таъсири тарафҳо дошта бошанд?" қисмат.
Албатта! Идоракунандагони ҳодиса беҳтарин ҷой барои таъсири тарафҳо мебошанд.
Манбаи дигари олие, ки ман мехоҳам зикр кунам, ин Куҷо шумо метавонед таъсири тарафҳо эҷод кунед
Дар React, таъсири тарафҳо одатан дар дохили идоракунандагони ҳодиса ҷойгир карда мешаванд.
Агар шумо ҳамаи имконоти дигарро истифода карда бошед ва идоракунандаи ҳодисаи дурустро барои таъсири тарафҳои худ пайдо карда натавонед, шумо то ҳол метавонед онро ба JSX-и баргардондашуда бо истифода аз даъвати useEffect дар компоненти худ пайваст кунед. Ин ба React мегӯяд, ки онро баъдтар, пас аз рендер кардан, вақте ки таъсири тарафҳо иҷозат дода мешавад, иҷро кунад. Аммо, ин роҳ бояд роҳи охирини шумо бошад.
"Таъсир дар беруни рендер рух медиҳад" - Дэвид Хоуршид.
(state) => UI
(state, event) => nextState // 🤔 Таъсирҳо?
UI як функсияи ҳолат аст. Ҳамчун ҳамаи ҳолатҳои ҷорӣ рендер карда мешаванд, он UI-и ҷорӣро ба вуҷуд меорад. Ҳамин тавр, вақте ки ҳодиса рух медиҳад, он ҳолати нав эҷод мекунад. Ва вақте ки ҳолат тағир меёбад, он UI-и нав месозад. Ин парадигма ядрои React аст.
Таъсирҳо кай рух медиҳанд?
Гузаришҳои ҳолат. Ҳамеша.
(state, event) => nextState
|
V
(state, event) => (nextState, effect) // Ин ҷо
![Тасвири мушаххаси рендер](https://media.slid.es/uploads/174419/images/9663683/CleanShot_2022-06-22_at_20.24.08_2x.png align="left")
Таъсири амал куҷо меравад? Идоракунандагони ҳодиса. Гузаришҳои ҳолат.
Ки ҳамзамон бо идоракунандагони ҳодиса иҷро мешаванд.
Мо шояд ба Таъсирҳо ниёз надорем
Мо метавонем useEffect-ро истифода барем, зеро намедонем, ки API-и сохташуда аз React вуҷуд дорад, ки метавонад ин мушкилро ҳал кунад.
Инҷо як манбаи олие барои хондани ин мавзӯъ мавҷуд аст: Шояд ба шумо таъсир лозим набошад
Мо ба useEffect барои табдил додани маълумот ниёз надорем.
useEffect ➡️ useMemo (гарчанде ки мо дар аксари ҳолатҳо ба useMemo ниёз надорем)
const Cart = () => {
const [items, setItems] = useState([])
const [total, setTotal] = useState(0)
useEffect(() => {
setTotal(items.reduce((total, item) => total + item.price, 0))
}, [items])
// ...
}
Онро бодиққат 🧐 хонед ва боз фикр кунед.
const Cart = () => {
const [items, setItems] = useState([])
const total = useMemo(() => {
return items.reduce((total, item) => total + item.price, 0)
}, [items])
// ...
}
Ба ҷои истифодаи useEffect
барои ҳисоб кардани маҷмӯӣ, мо метавонем useMemo
-ро барои мемоизатсияи маҷмӯӣ истифода барем. Ҳатто агар мутағир ҳисоби гаронбаҳо набошад ҳам, ба мо лозим нест, ки useMemo
-ро барои мемоизатсияи он истифода барем, зеро мо дар асл самаранокӣ барои хотираро иваз мекунем.
Ҳар вақте ки мо setState
-ро дар useEffect
мебинем, ин як нишонаи огоҳкунанда аст, ки мо метавонем онро содда кунем.
useSyncExternalStore
Таъсир бо мағозаҳои беруна?useEffect ➡️ useSyncExternalStore
❌ Роҳи нодуруст:
const Store = () => {
const [isConnected, setIsConnected] = useState(true)
useEffect(() => {
const sub = storeApi.subscribe(({ status }) => {
setIsConnected(status === 'connected')
})
return () => {
sub.unsubscribe()
}
}, [])
// ...
}
✅ Беҳтарин роҳ:
const Store = () => {
const isConnected = useSyncExternalStore(
// 👇 обуна шавед
storeApi.subscribe,
// 👇 скриншатро гиред
() => storeApi.getStatus() === 'connected',
// 👇 скриншати серверро гиред
true
)
// ...
}
Мо ба useEffect барои муошират бо волидайн ниёз надорем.
useEffect ➡️ eventHandler
❌ Роҳи нодуруст:
const ChildProduct = ({ onOpen, onClose }) => {
const [isOpen, setIsOpen] = useState(false)
useEffect(() => {
if (isOpen) {
onOpen()
} else {
onClose()
}
}, [isOpen])
return (
<div>
<button
onClick={() => {
setIsOpen(!isOpen)
}}
>
Дидани зудтаринро кушодан / бастани
</button>
</div>
)
}
📈 Роҳи беҳтар:
const ChildProduct = ({ onOpen, onClose }) => {
const [isOpen, setIsOpen] = useState(false)
const handleToggle = () => {
const nextIsOpen = !isOpen;
setIsOpen(nextIsOpen)
if (nextIsOpen) {
onOpen()
} else {
onClose()
}
}
return (
<div>
<button
onClick={}
>
Дидани зудтаринро кушодан / бастани
</button>
</div>
)
}
✅ Беҳтарин роҳ эҷоди хуки фармоишӣ аст:
const useToggle({ onOpen, onClose }) => {
const [isOpen, setIsOpen] = useState(false)
const handleToggle = () => {
const nextIsOpen = !isOpen
setIsOpen(nextIsOpen)
if (nextIsOpen) {
onOpen()
} else {
onClose()
}
}
return [isOpen, handleToggle]
}
const ChildProduct = ({ onOpen, onClose }) => {
const [isOpen, handleToggle] = useToggle({ onOpen, onClose })
return (
<div>
<button
onClick={handleToggle}
>
Дидани зудтаринро кушодан / бастани
</button>
</div>
)
}
Мо ба useEffect барои таъсиси singleton-ҳои глобалӣ ниёз надорем.
useEffect ➡️ justCallIt
❌ Роҳи нодуруст:
const Store = () => {
useEffect(() => {
storeApi.authenticate() // 👈 Ин ду маротиба иҷро мешавад!
}, [])
// ...
}
🔨 Биёед онро ислоҳ кунем:
const Store = () => {
const didAuthenticateRef = useRef()
useEffect(() => {
if (didAuthenticateRef.current) return
storeApi.authenticate()
didAuthenticateRef.current = true
}, [])
// ...
}
➿ Роҳи дигар:
let didAuthenticate = false
const Store = () => {
useEffect(() => {
if (didAuthenticate) return
storeApi.authenticate()
didAuthenticate = true
}, [])
// ...
}
🤔 Чӣ мешавад, агар:
storeApi.authenticate()
const Store = () => {
// ...
}
🍷 SSR, ҳа?
if (typeof window !== 'undefined') {
storeApi.authenticate()
}
const Store = () => {
// ...
}
🧪 Санҷиш?
const renderApp = () => {
if (typeof window !== 'undefined') {
storeApi.authenticate()
}
appRoot.render(<Store />)
}
Мо ҳатман ҳама чизро дар дохили компонент ҷойгир кардан лозим нест.
Мо ба useEffect барои дастрасӣ ба маълумот ниёз надорем.
useEffect ➡️ renderAsYouFetch (SSR) ё useSWR (CSR)
❌ Роҳи нодуруст:
const Store = () => {
const [items, setItems] = useState([])
useEffect(() => {
let isCanceled = false
getItems().then((data) => {
if (isCanceled) return
setItems(data)
})
return () => {
isCanceled = true
}
})
// ...
}
💽 Роҳи Remix:
import { useLoaderData } from '@renix-run/react'
import { json } from '@remix-run/node'
import { getItems } from './storeApi'
export const loader = async () => {
const items = await getItems()
return json(items)
}
const Store = () => {
const items = useLoaderData()
// ...
}
export default Store
⏭️🧹 Next.js (appDir) бо async/await дар Server Component:
// app/page.tsx
async function getData() {
const res = await fetch('https://api.example.com/...')
// Арзиши баргардондашуда *сериалӣ* нест
// Шумо метавонед Date, Map, Set ва ғайраро баргардонед.
// Тавсия: хатогиҳоро коркард кунед
if (!res.ok) {
// Ин Error Boundary-и наздиктаринро фаъол мекунад
throw new Error('Дастрасӣ ба маълумот муваффақ нашуд')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
⏭️💁 Next.js (appDir) бо useSWR дар Client Component:
// app/page.tsx
import useSWR from 'swr'
export default function Page() {
const { data, error } = useSWR('/api/data', fetcher)
if (error) return <div>дастрасӣ муваффақ нашуд</div>
if (!data) return <div>боргирӣ...</div>
return <div>салом {data}!</div>
}
⏭️🧹 Next.js (pagesDir) дар роҳи SSR:
// pages/index.tsx
import { GetServerSideProps } from 'next'
export const getServerSideProps: GetServerSideProps = async () => {
const res = await fetch('https://api.example.com/...')
const data = await res.json()
return {
props: {
data,
},
}
}
export default function Page({ data }) {
return <div>салом {data}!</div>
}
⏭️💁 Next.js (pagesDir) дар роҳи CSR:
// pages/index.tsx
import useSWR from 'swr'
export default function Page() {
const { data, error } = useSWR('/api/data', fetcher)
if (error) return <div>дастрасӣ муваффақ нашуд</div>
if (!data) return <div>боргирӣ...</div>
return <div>салом {data}!</div>
}
🍃 React Query (SSR way:
import { getItems } from './storeApi'
import { useQuery } from 'react-query'
const Store = () => {
const queryClient = useQueryClient()
return (
<button
onClick={() => {
queryClient.prefetchQuery('items', getItems)
}}
>
Ашёро бубинед
</button>
)
}
const Items = () => {
const { data, isLoading, isError } = useQuery('items', getItems)
// ...
}
⁉️ Дар ҳақиқат ⁉️ Мо бояд кадомро истифода барем? useEffect? useQuery? useSWR?
ё... танҳо use() 🤔
use() як функсияи нави React аст, ки ба таври консептуалӣ шабеҳ ба await як ваъдаро қабул мекунад. use() ваъдаеро, ки аз ҷониби функсия баргардонда шудааст, ба таври мувофиқ бо компонентҳо, хукҳо ва Suspense коркард мекунад. Дар бораи use() дар RFC-и React бештар маълумот гиред.
function Note({ id }) {
// Ин як огоҳномаро ба таври асинхронӣ дастрас мекунад, аммо барои муаллифи компонент, он мисли амалиёти синхронӣ ба назар мерасад.
const note = use(fetchNote(id))
return (
<div>
<h1>{note.title}</h1>
<section>{note.body}</section>
</div>
)
}
Мушкилот дар дастрасӣ ба маълумот дар useEffect
🏃♂️ Ҳолатҳои рақобат
🔙 Тугмаи пушти бозгашт дарҳол нест
🔍 Ҳеҷ гуна SSR ё мундариҷаи аввалини HTML нест
🌊 Ҷустуҷӯи шакаста
- Reddit, Дэн Абрамов
Хулоса
Аз дастрасӣ ба маълумот то мубориза бо API-ҳои императивӣ, таъсири тарафҳо яке аз манбаъҳои муҳимтарини нороҳатии рушди барномаҳои вебӣ мебошад. Ва биёед ростқавл бошем, ҷойгир кардани ҳама чиз дар хукҳои useEffect танҳо каме кӯмак мекунад. Хушбахтона, илм (хуб, математика) барои таъсири тарафҳо вуҷуд дорад, ки дар мошинҳои ҳолат ва диаграммаҳои ҳолат расман тасдиқ шудааст, ки метавонад ба мо кӯмак кунад, ки тарзи танзим кардани таъсирҳоро, новобаста аз он ки онҳо чӣ қадар мураккаб шаванд, ба таври эъломи тасвир кунем ва фаҳмем.