Weşand li ser

Silav React, Silav useEffect (Înshallah)

Nivîskar

Di vê gotarê de, ez ê nîşanî we bidim ka çawa React bikar bînin da ku useEffect di pir rewşan de biguherînin.

Ez "Goodbye, useEffect" ji hêla David Khoursid ve temaşe dikim û ew 🤯 hişê min di awayekî 😀 baş de dihejîne. Ez razî me ku useEffect ewqas tê bikaranîn ku ew koda me qirêj û zehmet dike ku meriv wê bigire. Ez demek dirêj e ku useEffect bikar tînim û ez sûcdar im ku ez wê şaş bikar tînim. Ez piştrast im ku React taybetmendiyên xwe hene ku dê koda min paqijtir û hêsantir bigire.

useEffect çi ye?

useEffect hûkek e ku destûrê dide me ku em bandorên alî di pêkhateyên fonksiyonê de pêk bînin. Ew componentDidMount, componentDidUpdate, û componentWillUnmount di APIyek yekane de kom dike. Ew hûkek e ku dê bihêle ku em gelek tişt bikin. Lê ew hûkek e ku gelek xeter e û dikare gelek xeletiyên diyar bike.

Çima useEffect xeter e?

Werin em li mînaka jêrîn binêrin:

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>
}

Ew hejmareke hêsan e ku her çend saniyekê zêde dibe. Ew useEffect bikar tîne da ku navberekê destnîşan bike. Ew her weha useEffect bikar tîne da ku navberê paqij bike dema ku pêkhate dest ji xwe berdide. Beşê koda li jor mînakek pir belav e ji bo useEffect. Ew mînakek hêsan e, lê ew mînakek pir xirab jî ye.

Pirsgirêk bi vê mînakê ev e ku navber her carê ku pêkhate dîsa tê xwendin, tê destnîşankirin. Ger pêkhate ji ber her sedemekî dîsa were xwendin, navber dê dîsa were destnîşankirin. Navber dê her çend saniyekê du caran bê bang kirin. Ew bi vê mînaka hêsan re ne pirsgirêk e, lê dikare bibe pirsgirêk ji bo dema ku navber aloztir be. Ew dikare bibe sedema xistina bîranînê jî.

Çawa çêtir bikin?

Gelek rê hene ku meriv vê pirsgirêkê çareser bike. Yek rê ew e ku useRef bikar bîne da ku navber bihêle.

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>
}

Koda li jor pir baştir e ji mînaka berê. Ew navberê her carê ku pêkhate dîsa tê xwendin, destnîşan nake. Lê ew hîn jî hewceyê baştirîn e. Ew hîn jî hinekî tevlihev e. Û ew hîn jî useEffect bikar tîne, ku hûkek e ku pir xeter e.

useEffect ji bo bandorên alî nîne

Wekî ku em di derbarê useEffect de dizanin, ew componentDidMount, componentDidUpdate, û componentWillUnmount di APIyek yekane de kom dike. Werin em hin mînakên wê bidin:

useEffect(() => {
  // componentDidMount?
}, [])
useEffect(() => {
  // componentDidUpdate?
}, [something, anotherThing])
useEffect(() => {
  return () => {
    // componentWillUnmount?
  }
}, [])

Ew hêsan e ku meriv wê fêm bike. useEffect tê bikaranîn da ku bandorên alî pêk bîne dema ku pêkhate tê danîn, nûkirin, û dest ji xwe berdide. Lê ew ne tenê ji bo pêkanîna bandorên alî tê bikaranîn. Ew her weha tê bikaranîn da ku bandorên alî pêk bîne dema ku pêkhate dîsa tê xwendin. Ew fikirê baş nîne ku meriv bandorên alî pêk bîne dema ku pêkhate dîsa tê xwendin. Ew dikare gelek xeletiyên diyar bike. Ew baştir e ku meriv hûkên din bikar bîne da ku bandorên alî pêk bîne dema ku pêkhate dîsa tê xwendin.

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>Hejmara guhertinan: {count}</div>
    </div>
  )
}

useEffect setkera rewşê nîne

import React, { useState, useEffect } from 'react'

const Example = () => {
  const [count, setCount] = useState(0)

  // Bi componentDidMount û componentDidUpdate re dişibihe:
  useEffect(() => {
    // Sernavê belgeyê bi karanîna APIya gerokê nû bike
    document.title = `Te ${count} caran tikandin`
  }) // <-- ev e pirsgirêk, 😱 ew dîwarê girêdanê winda kiriye

  return (
    <div>
      <p>Te {count} caran tikandin</p>
      <button onClick={() => setCount(count + 1)}>Min bikine</button>
    </div>
  )
}

Ez pêşniyar dikim ku hûn vê belgeyê bixwînin: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

Imperative vs Declarative

Imperative: Dema ku tiştek çêbibe, vê bandorê pêk bîne.

Declarative: Dema ku tiştek çêbibe, ew ê rewşê biguherîne û li gorî (dîwarê girêdanê) kîjan parçeyên rewşê hatine guhertin, ev bandor divê bê pêkanîn, lê tenê ger şert û mercên taybetî hebin. Û React dibe ku ew ji bo tu sedemekê xwendina hevserî dîsa pêk bîne.

Concept vs Implementation

Concept:

useEffect(() => {
  doSomething()

  return () => cleanup()
}, [whenThisChanges])

Implementation:

useEffect(() => {
  if (foo && bar && (baz || quo)) {
    doSomething()
  } else {
    doSomethingElse()
  }

  // oops, ez paqijkirinê ji bîr kir
}, [foo, bar, baz, quo])

Pêkanîna rastîn:

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])

Ew tirsnak e ku meriv vê kodê binivîse. Herweha, ew ê di bingeha koda me de normal be û têk biçe. 😱🤮

Bandorên alî ku derê diçin?

React 18 bandorên alî du caran di danînê de pêk tîne (di moda hişk de). Danîn/bandor (╯°□°)╯︵ ┻━┻ -> Dest ji xwe berdan (şebihkirin)/paqijkirin ┬─┬ /( º _ º /) -> Dîsa danîn/bandor (╯°□°)╯︵ ┻━┻

Ew divê li derveyî pêkhateyê bê danîn? useEffectya standard? Û ... xerîb e. Hmm ... 🤔 Em nikarin ew di xwendinê de bidin, ji ber ku bandorên alî divê li wir nebin, ji ber ku xwendin wekî destê rastê yeketiyek matematikî ye. Ew divê tenê encama hesabê be.

useEffect ji bo çi ye?

Hevdemkirin

useEffect(() => {
  const sub = createThing(input).subscribe((value) => {
    // tiştek bi nirxê bikin
  })

  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)
  }
}, [])

Bandorên çalakiyê vs Bandorên çalakî

 Fire-and-forget            Hevdemkirin
 (Bandorên çalakiyê)        (Bandorên çalakî)

        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-------------------------------------------------------------------------------->
                                       Dest ji xwe berdan      Dîsa danîn

Bandorên çalakiyê ku derê diçin?

Rêvebirên bûyeran. Sorta.

<form
  onSubmit={(event) => {
    // 💥 bandorê alî!
    submitData(event)
  }}
>
  {/* ... */}
</form>

Agirîyek mezin di Beta React.js de heye. Ez pêşniyar dikim ku hûn wê bixwînin. Bi taybetî "Ma rêvebirên bûyeran dikarin bandorên alî hebin?" beşa.

Bi tevahî! Rêvebirên bûyeran cihê çêtirîn in ji bo bandorên alî.

Çavkaniyek din a baş ku ez dixwazim behsa wê bikim, Li ku derê hûn dikarin bandorên alî pêk bînin

Di Reactê de, bandorên alî bi gelemperî di nav rêvebirên bûyeran de cih digirin.

Ger we hemû bijartên din xerç kirine û hûn nikarin rêvebira bûyerê ya rast ji bo bandora xwe ya alî bibînin, hûn hîn jî dikarin wê bi karanîna useEffect di pêkhateya xwe de bi JSX ya xwe ya vegerandî ve girêdin. Ev ji React re dibêje ku ew paşê, piştî xwendinê pêk bîne, dema ku bandorên alî destûr tê dayîn. Lêbelê, ev rêbaz divê bijartina we ya dawî be.

"Bandorên alî li derveyî xwendinê çêdibin" - David Khoursid.

(state) => UI
(state, event) => nextState // 🤔 Bandorên alî?

UI fonksiyonê rewşê ye. Ji ber ku hemû rewşên heyî tên xwendin, ew ê UIya heyî çêbike. Bi heman awayî, dema ku bûyerek çêbibe, ew ê rewşek nû çêbike. Û dema ku rewş diguhere, ew ê UIyek nû çêbike. Ev paradigmaya bingehîn a Reactê ye.

Bandorên alî kengê çêdibin?

Navîn? 🕵️ Rêvebir? 🤙 Sagas? 🧙‍♂️ Reaksiyon? 🧪 Sink? 🚰 Monads(?) 🧙‍♂️ Her dem? 🤷‍♂️

Veguheztinên rewşê. Her dem.

(state, event) => nextState
          |
          V
(state, event) => (nextState, effect) // Li vir

![Wêneya şiroveyê ya dîsa xwendinê](https://media.slid.es/uploads/174419/images/9663683/CleanShot_2022-06-22_at_20.24.08_2x.png align="left")

Bandorên çalakiyê ku derê diçin? Rêvebirên bûyeran. Veguheztinên rewşê.

Ku bi heman demê de bi rêvebirên bûyeran re tê pêkanîn.

Em dibe ku hewceyê bandorên alî nebin

Em dikarin useEffect bikar bînin ji ber ku em nizanin ku APIyek çêkirî ji React heye ku dikare vê pirsgirêkê çareser bike.

Li vir çavkaniyek pir baş heye ku meriv li ser vê mijarê bixwîne: Dibe ku hûn hewceyê bandorekê nebin

Me hewceyê useEffectê ji bo veguherandina daneyan nîne.

useEffect ➡️ useMemo (her çend di pir rewşan de me hewceyê useMemo nîne)

const Cart = () => {
  const [items, setItems] = useState([])
  const [total, setTotal] = useState(0)

  useEffect(() => {
    setTotal(items.reduce((total, item) => total + item.price, 0))
  }, [items])

  // ...
}

Dîsa bi baldarî bixwînin û bifikirin 🧐.

const Cart = () => {
  const [items, setItems] = useState([])
  const total = useMemo(() => {
    return items.reduce((total, item) => total + item.price, 0)
  }, [items])

  // ...
}

Li şûna ku useEffect bikar bînin ji bo hesabkirina tevahî, em dikarin useMemo bikar bînin ji bo memokirina tevahî. Her çend guherto hesabkirineke biha nîne, lê me hewceyê useMemo nîne ku wê memo bike ji ber ku em bi esasî performansê bi bîranînê diguherînin.

Her carê ku em setState di useEffect de dibînin, ew nîşanek hişyarî ye ku em dikarin wê hêsantir bikin.

Bandorên alî bi depoyên derveyî re? useSyncExternalStore

useEffect ➡️ useSyncExternalStore

❌ Rêya çewt:

const Store = () => {
  const [isConnected, setIsConnected] = useState(true)

  useEffect(() => {
    const sub = storeApi.subscribe(({ status }) => {
      setIsConnected(status === 'connected')
    })

    return () => {
      sub.unsubscribe()
    }
  }, [])

  // ...
}

✅ Rêya çêtirîn:

const Store = () => {
  const isConnected = useSyncExternalStore(
    // 👇 abone bibin
    storeApi.subscribe,
    // 👇 snapshotê bistînin
    () => storeApi.getStatus() === 'connected',
    // 👇 snapshotê serverê bistînin
    true
  )

  // ...
}

Me hewceyê useEffectê ji bo komunîkasyonê bi dêûbav re nîne.

useEffect ➡️ rêvebira bûyeran

❌ Rêya çewt:

const ChildProduct = ({ onOpen, onClose }) => {
  const [isOpen, setIsOpen] = useState(false)

  useEffect(() => {
    if (isOpen) {
      onOpen()
    } else {
      onClose()
    }
  }, [isOpen])

  return (
    <div>
      <button
        onClick={() => {
          setIsOpen(!isOpen)
        }}
      >
        Dîmena bilez veguherîne
      </button>
    </div>
  )
}

📈 Rêya çêtir:

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={}
      >
        Dîmena bilez veguherîne
      </button>
    </div>
  )
}

✅ Rêya çêtirîn ev e ku hûn hûkekê ya taybetî çêbikin:

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}
      >
        Dîmena bilez veguherîne
      </button>
    </div>
  )
}

Me hewceyê useEftê ji bo destpêkkirina yekanetiyên global nîne.

useEffect ➡️ justCallIt

❌ Rêya çewt:

const Store = () => {
  useEffect(() => {
    storeApi.authenticate() // 👈 Ev ê du caran bê pêkanîn!
  }, [])

  // ...
}

🔨 Werin em wê rast bikin:

const Store = () => {
  const didAuthenticateRef = useRef()

  useEffect(() => {
    if (didAuthenticateRef.current) return

    storeApi.authenticate()

    didAuthenticateRef.current = true
  }, [])

  // ...
}

➿ Rêyek din:

let didAuthenticate = false

const Store = () => {
  useEffect(() => {
    if (didAuthenticate) return

    storeApi.authenticate()

    didAuthenticate = true
  }, [])

  // ...
}

🤔 Çawa eger:

storeApi.authenticate()

const Store = () => {
  // ...
}

🍷 SSR, huh?

if (typeof window !== 'undefined') {
  storeApi.authenticate()
}
const Store = () => {
  // ...
}

🧪 Testkirin?

const renderApp = () => {
  if (typeof window !== 'undefined') {
    storeApi.authenticate()
  }

  appRoot.render(<Store />)
}

Me hewceyê ku her tişt di nav pêkhateyek de bidin, nîne.

Me hewceyê useEffectê ji bo dakêşana daneyan nîne.

useEffect ➡️ renderAsYouFetch (SSR) an useSWR (CSR)

❌ Rêya çewt:

const Store = () => {
  const [items, setItems] = useState([])

  useEffect(() => {
    let isCanceled = false

    getItems().then((data) => {
      if (isCanceled) return

      setItems(data)
    })

    return () => {
      isCanceled = true
    }
  })

  // ...
}

💽 Rêya 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) bi async/await di Pêkhateya Serverê de:

// app/page.tsx
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // Nirxa vegerandî *nenûsî* ye
  // Hûn dikarin Date, Map, Set, hwd. vegerînin

  // Pêşniyar: çewtiyên bixwînin
  if (!res.ok) {
    // Ev ê Nûçeya Xeletiyê ya herî nêzîk çalak bike
    throw new Error('Nexwest ku daneyên dakêşin')
  }

  return res.json()
}

export default async function Page() {
  const data = await getData()

  return <main></main>
}

⏭️💁 Next.js (appDir) bi useSWR di Pêkhateya Klientê de:

// app/page.tsx
import useSWR from 'swr'

export default function Page() {
  const { data, error } = useSWR('/api/data', fetcher)

  if (error) return <div>nexwest ku barkirin</div>
  if (!data) return <div>dibare...</div>

  return <div>selamu {data}!</div>
}

⏭️🧹 Next.js (pagesDir) di SSR de:

// 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>selamu {data}!</div>
}

⏭️💁 Next.js (pagesDir) di CSR de:

// pages/index.tsx
import useSWR from 'swr'

export default function Page() {
  const { data, error } = useSWR('/api/data', fetcher)

  if (error) return <div>nexwest ku barkirin</div>
  if (!data) return <div>dibare...</div>

  return <div>selamu {data}!</div>
}

🍃 React Query (Rêya SSR):

import { getItems } from './storeApi'
import { useQuery } from 'react-query'

const Store = () => {
  const queryClient = useQueryClient()

  return (
    <button
      onClick={() => {
        queryClient.prefetchQuery('items', getItems)
      }}
    >
      Tiştên bibînin
    </button>
  )
}

const Items = () => {
  const { data, isLoading, isError } = useQuery('items', getItems)

  // ...
}

⁉️ Bi rastî ⁉️ Em divê çi bikar bînin? useEffect? useQuery? useSWR?

an... tenê use() 🤔

use() fonksiyonek nû ya Reactê ye ku sopek qebûl dike, bi konseptek wekî await re dişibihe. use() sopekê ku ji hêla fonksiyonê ve tê vegerandin, bi awayekî ku bi pêkhateyan, hûkan, û Suspense re hevbeş e, rêve dibe. Zêdetir di derbarê use() de di RFC ya Reactê de hîn bibin.

function Note({ id }) {
  // Ev nîşek bi awayekî asenkron dakêşîne, lê ji bo nivîskarê pêkhateyê, ew wekî xebatek senkronîk xuya dike.
  const note = use(fetchNote(id))
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
    </div>
  )
}

Pirsgirêkên dakêşana daneyan di useEffectê de

🏃‍♂️ Şert û mercên pêşbirkê

🔙 Tu vegera tavilê ya bişkojka paşê tune ye

🔍 Tu naveroka SSR an HTML ya destpêkê tune ye

🌊 Şopa şeraba avê

  • Reddit, Dan Abramov

Encam

Ji dakêşana daneyan heya şerê bi APIyên imperative re, bandorên alî yek ji çavkaniyên herî girîng yên xemgînîyê ne di pêşxistina sepanên webê de. Û werin em rast bin, danîna her tiştî di nav hûkên useEffect de tenê hinekî alîkarî dike. Bi şikir, zanistek (baş, matematik) ji bo bandorên alî heye, ku di makîneyên rewşê û şert û mercên rewşê de tê nîşankirin, ku dikare ji me re bibe alîkar ku em model û fêm bikin ka çawa bandorên alî rêkûpêk bikin, bêyî ku ew çiqas tevlihev bin, bi awayekî eşkere.

Çavkaniyên