Waxaa la daabacay

Salaan React, Salaan useEffect (Eebbe ka soo degay, sidaas ha ahaato)

Qorayaasha

Maqaalkan, waxaan ku tusayaa sida loo isticmaalo React si loogu beddelo useEffect kiisa badan.

Waxaan daawanayay "Goodbye, useEffect" by David Khoursid, waxaana aad ula yaabay 🤯 si wanaagsan 😜. Waxaan oggolahay in useEffect si aad ah loogu isticmaalo taasoo keenta in koodkeena uu wasakh noqdo oo adag yahay in la ilaaliyo. Waqti dheer ayaan isticmaalayay useEffect, waxaana qirtay inaan si khaldan u isticmaalay. Waxaan hubaa in React ay leedahay astaamo ka dhigi doona koodkeyga nadiif oo fudud in la ilaaliyo.

Maxaa useEffect ah?

useEffect waa qalab (hook) noo oggolaanaya inaan sameyno ficilo dhinac (side effects) gudaha qaybaha shaqada. Waxay isku daraa componentDidMount, componentDidUpdate, iyo componentWillUnmount hal API. Waa qalab cajiib ah oo noo oggolaanaya inaan wax badan sameyno. Laakiin sidoo kale waa qalab aad u khatar ah oo keeni kara khaladaad badan.

Maxaa useEffect khatar u ah?

Aan eegno tusaalaha soo socda:

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

Waa tiro fudud oo kordhaysa ilbiriqsi kasta. Waxay isticmaashaa useEffect si loo dejiyo wakhti go'an (interval). Waxay sidoo kale isticmaashaa useEffect si loo nadiifiyo wakhtiga go'an marka qaybtu ka baxdo. Qaybta koodka ee kore waa kiis isticmaalka caadiga ah ee useEffect. Waa tusaale fudud, laakiin sidoo kale waa tusaale xun.

Dhibaatada tusaalahaani waa in wakhtiga go'an la dejiyo mar kasta oo qaybtu dib u soo celiso. Haddii qaybtu dib u soo celiso sabab kasta oo ay tahay, wakhtiga go'an ayaa mar labaad la dejin doonaa. Wakhtiga go'an ayaa la wici doonaa laba jeer hal ilbiriqsi. Ma aha dhibaato oo ku saabsan tusaalaha fudud, laakiin waxay noqon kartaa dhibaato weyn marka wakhtiga go'an uu ka sii adag yahay. Waxay sidoo kale keeni kartaa luminta xusuusta (memory leaks).

Sida loo hagaajiyo?

Waxaa jira siyaabo badan oo loo hagaajin karo dhibaatadaani. Hal hab ayaa ah in la isticmaalo useRef si loo kaydiyo wakhtiga go'an.

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

Koodka kore waa ka fiican yahay tusaalaha hore. Kuma dejinayo wakhtiga go'an mar kasta oo qaybtu dib u soo celiso. Laakiin wali waxaa u baahan in la hagaajiyo. Wali waa dhib yar. Iyo wali waxay isticmaashaa useEffect, kaas oo ah qalab aad u khatar ah.

useEffect ma aha ficilo (effects)

Sidaan ognahay ku saabsan useEffect, waxay isku daraa componentDidMount, componentDidUpdate, iyo componentWillUnmount hal API. Aan siino tusaalooyin:

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

Way fududahay in la fahmo. useEffect waxaa loo isticmaalaa in la sameeyo ficilo dhinac marka qaybtu soo baxdo, dib u cusbooneysiiso, ama ka baxdo. Laakiin ma aha oo keliya in loo isticmaalo in la sameeyo ficilo dhinac. Waxaa loo isticmaalaa in la sameeyo ficilo dhinac marka qaybtu dib u soo celiso. Ma aha fikrad fiican in la sameeyo ficilo dhinac marka qaybtu dib u soo celiso. Waxay keeni kartaa khaladaad badan. Waa ka wanaagsan tahay in la isticmaalo qalab kale si loo sameeyo ficilo dhinac marka qaybtu dib u soo celiso.

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>Number of changes: {count}</div>
    </div>
  )
}

useEffect ma aha dejinta xaaladda (state setter)

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

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

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`
  }) // <-- this is the problem, 😱 it's missing the dependency array

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  )
}

Waxaan kugula talinayaa inaad akhrido dukumentigan: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

Amarro (Imperative) vs Sharci (Declarative)

Amarro: Marka wax dhacaan, fulin ficilkan.

Sharci: Marka wax dhacaan, waxay keeni doontaa isbeddelka xaaladda, oo waxay ku xiran tahay (dependency array) qaybaha xaaladda ee isbedelay, ficilkan waa in la fulinayaa, laakiin haddii xaalad gaar ah ay tahay run. Iyo React ayaa laga yaabaa inay mar labaad fuliso iyada oo aan wax sabab ah lahayn sabab la'aan dib u soo celinta isku mid ah.

Fikrad vs Hirgelin

Fikrad:

useEffect(() => {
  doSomething()

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

Hirgelin:

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

  // oops, I forgot the cleanup
}, [foo, bar, baz, quo])

Hirgelinta dhabta ah:

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

Waa wax cabsi leh in la qoro koodkan. Intaa waxaa dheer, waxay noqon doontaa caadi ah in koodkeena lagu qasaaro 😱🤮.

Xagee ayay ficiladu tagaan?

React 18 waxay fulisaa ficilada laba jeer marka la dejiyo (strict mode). Mount/effect (╯°□°)╯︵ ┻━┻ -> Unmount (simulated)/cleanup ┬─┬ /( º _ º /) -> Remount/effect (╯°□°)╯︵ ┻━┻

Ma waa in lagu meeleeyaa dibadda qaybta? Isticmaalka caadiga ah ee useEffect? Uh... xishood. Hmm... 🤔 Ma aanan gelin karin gudaha render sababtoo ah waa in aan lahayn wax ficilo dhinac ah halkaas sababtoo ah render kaliya waxay la mid tahay gacanta midig ee equation xisaab. Waa in ay noqoto natiijada xisaabinta oo keliya.

Maxaa loogu talagalay useEffect?

Iswaafajinta (Synchronization)

useEffect(() => {
  const sub = createThing(input).subscribe((value) => {
    // do something with 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)
  }
}, [])

Ficilada dhaqdhaqaaqa (Action effects) vs Ficilada hawlaha (Activity effects)

 Fire-and-forget            Synchronized
 (Action effects)        (Activity effects)

        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-------------------------------------------------------------------------------->
                                       Unmount      Remount

Xagee ayay ficilada dhaqdhaqaaqa tagaan?

Gacan-qabashada dhacdooyinka (Event handlers). Sidaas oo kale.

<form
  onSubmit={(event) => {
    // 💥 side-effect!
    submitData(event)
  }}
>
  {/* ... */}
</form>

Waxaa jira macluumaad fiican oo ku jira Beta React.js. Waxaan kugula talinayaa inaad akhrido. Gaar ahaan "Miyaan gacan-qabashada dhacdooyinka ku yeelan karaa ficilo dhinac? Qaybta.

Dabcan! Gacan-qabashada dhacdooyinka waa meesha ugu fiican ee lagu sameeyo ficilada dhinac.

Ikhtiyaar kale oo weyn oo aan rabbo inaan xuso waa Meesha aad ku samayn karto ficilada dhinac

Gudaha React, ficilada dhinac badanaa waxay ku jiraan gudaha gacan-qabashada dhacdooyinka.

Haddii aad dhammaystirtay dhammaan ikhtiyaaraadka kale oo aadan helin gacan-qabashada dhacdada saxda ah ee ficilka dhinacaaga, wali waxaad ku dhejin kartaa JSX-gaaga oo aad ku soo celiso wicitaan useEffect gudaha qaybtaada. Tani waxay u sheegaysaa React inay fuliso ka dib markii la soo bandhigo, marka la oggolaado in ficilada dhinac la sameeyo. Si kastaba ha ahaatee, habkan waa in uu noqdaa ikhtiyaarkaaga ugu dambeeya.

"Ficilada waxay dhacaan dibadda soo bandhigidda" - David Khoursid.

(state) => UI
(state, event) => nextState // 🤔 Effects?

UI waa shaqada xaaladda. Sida dhammaan xaaladaha hadda jira loo soo bandhigo, waxay soo saari doontaa UI hadda jira. Si la mid ah, marka dhacdo dhacdo, waxay abuuri doontaa xaalad cusub. Marka xaaladda isbeddesho, waxay dhisi doontaa UI cusub. Habkan waa nuxurka React.

Goorma ayay ficilada dhacaan?

Bartamaha (Middleware)? 🕵️ Kaas oo ku noqda (Callbacks)? 🤙 Sagas? 🧙‍♂️ Fal-celin (Reactions)? 🧪 Dhiibashada (Sinks)? 🚰 Monads (?) 🧙‍♂️ Mar kasta? 🤷‍♂️

Isbeddelka xaaladda (State transitions). Mar walba.

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

![Sawir muujinaya dib u soo celinta](https://media.slid.es/uploads/174419/images/9663683/CleanShot_2022-06-22_at_20.24.08_2x.png align="left")

Xagee ayay ficilada dhaqdhaqaaqa tagaan? Gacan-qabashada dhacdooyinka. Isbeddelka xaaladda.

Kuwaas oo dhaca isla waqtiga gacan-qabashada dhacdooyinka.

Waxaa laga yaabaa inaan u baahnayn ficilada (effects)

Waxaan isticmaali karnaa useEffect sababtoo ah ma ogin in ay jirto API la dhisay oo ka timid React taas oo xalin karta dhibaatadaani.

Halkan waxaa ah ikhtiyaar fiican oo lagu akhriyo mawduucan: Waxaa laga yaabaa inaadan u baahnayn ficil

Ma u baahna useEffect si loogu beddelo xogta (transforming data).

useEffect ➡️ useMemo (xitaa haddii aanan u baahnayn useMemo kiisa badan)

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

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

  // ...
}

Akhri oo mar labaad si taxaddar leh u fiirso 🧐.

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

  // ...
}

Marka aan u isticmaalno useEffect si loo xisaabiyo wadarta, waxaan isticmaali karnaa useMemo si loo xasuusto wadarta. Xitaa haddii xisaabinta aysan qaadan waqti badan, ma u baahna inaan isticmaalno useMemo si loo xasuusto sababtoo ah waxaan u isticmaalaynaa waxqabadka badalkii xusuusta.

Mar kasta oo aan aragno setState gudaha useEffect, waa calaamad digniin ah inaan ka saari karno.

Ficilada oo leh dukaamo dibadda ah? useSyncExternalStore

useEffect ➡️ useSyncExternalStore

❌ Habka khaldan:

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

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

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

  // ...
}

✅ Habka ugu fiican:

const Store = () => {
  const isConnected = useSyncExternalStore(
    // 👇 subscribe
    storeApi.subscribe,
    // 👇 get snapshot
    () => storeApi.getStatus() === 'connected',
    // 👇 get server snapshot
    true
  )

  // ...
}

Ma u baahna useEffect si loola xiriiro waalidka (communicating with parents).

useEffect ➡️ gacan-qabashada dhacdada (eventHandler)

❌ Habka khaldan:

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

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

  return (
    <div>
      <button
        onClick={() => {
          setIsOpen(!isOpen)
        }}
      >
        Toggle quick view
      </button>
    </div>
  )
}

📈 Habka ka fiican:

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={}
      >
        Toggle quick view
      </button>
    </div>
  )
}

✅ Habka ugu fiican waa in la abuuro qalab la habeeyay (custom hook):

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}
      >
        Toggle quick view
      </button>
    </div>
  )
}

Ma u baahna useEft si loo bilaabo ururinta caalamiga ah (initializing global singletons).

useEffect ➡️ justCallIt

❌ Habka khaldan:

const Store = () => {
  useEffect(() => {
    storeApi.authenticate() // 👈 This will run twice!
  }, [])

  // ...
}

🔨 Aan hagaajino:

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

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

    storeApi.authenticate()

    didAuthenticateRef.current = true
  }, [])

  // ...
}

➿ Hab kale:

let didAuthenticate = false

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

    storeApi.authenticate()

    didAuthenticate = true
  }, [])

  // ...
}

🤔 Sidee haddii:

storeApi.authenticate()

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

🍷 SSR, huh?

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

🧪 Imtixaan (Testing)?

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

  appRoot.render(<Store />)
}

Ma u baahna inaan dhigno wax walba gudaha qayb.

Ma u baahna useEffect si loogu soo qaado xogta (fetching data).

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

❌ Habka khaldan:

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

  useEffect(() => {
    let isCanceled = false

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

      setItems(data)
    })

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

  // ...
}

💽 Habka 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) oo leh async/await gudaha Server Component:

// app/page.tsx
async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.

  // Recommendation: handle errors
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }

  return res.json()
}

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

  return <main></main>
}

⏭️💁 Next.js (appDir) oo leh useSWR gudaha Client Component:

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

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

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>

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

⏭️🧹 Next.js (pagesDir) gudaha 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>hello {data}!</div>
}

⏭️💁 Next.js (pagesDir) gudaha CSR:

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

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

  if (error) return <div>failed to load</div>
  if (!data) return <div>loading...</div>

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

🍃 React Query (Habka SSR):

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

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

  return (
    <button
      onClick={() => {
        queryClient.prefetchQuery('items', getItems)
      }}
    >
      See items
    </button>
  )
}

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

  // ...
}

⁉️ Runtii ⁉️ Maxaa inaan isticmaalno? useEffect? useQuery? useSWR?

ama... just use() 🤔

use() waa shaqal cusub oo React ah oo qaata ballan oo si la mid ah u shaqeeya await. use() waxay maamushaa ballanka laga soo celiyo shaqada si waafaqsan qaybaha, qalabka, iyo Suspense. Baro wax badan oo ku saabsan use() gudaha React RFC.

function Note({ id }) {
  // This fetches a note asynchronously, but to the component author, it looks
  // like a synchronous operation.
  const note = use(fetchNote(id))
  return (
    <div>
      <h1>{note.title}</h1>
      <section>{note.body}</section>
    </div>
  )
}

Dhibaatooyinka la soo qaaday ee gudaha useEffect

🏃‍♂️ Xaaladaha tartan (Race conditions)

🔙 Ma jiro badhanka dib u soo celinta (back button)

🔍 Ma jiro SSR ama waxyaabaha HTML-ka ee hore

🌊 Raacida dhacdooyinka (Chasing waterfall)

  • Reddit, Dan Abramov

Gabagabo

Laga soo bilaabo soo qaadashada xogta ilaa dagaalka lagula jiro API-yada amarka (imperative), ficilada dhinac (side effects) waa mid ka mid ah ilo dareenka ugu badan ee horumarinta barnaamijyada shabakadda. Iyo waxaa ina xaqiijinaysa, dhigista wax walba gudaha qalabka useEffect waxay caawineysaa xoogaa yar oo keliya. Nasiib wanaag, waxaa jira saynis (weli, xisaab) oo ku saabsan ficilada dhinac, oo si rasmi ah loogu dhigay mishiinada xaaladda iyo jaantusyada xaaladda, kaas oo noo caawin kara inaan si muuqaal ah u naqshadeeyno oo aan u fahanno sida loo qaban karo ficilada, iyada oo aan loo eegin sida ay u adag yihiin oo loo qaban karo si sharci ah.

Ikhtiyaarrada