From f5900ef50d925514c6dd8382635265c1168f1321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rado=C5=A1=20Mili=C4=87ev?= <40705899+rammba@users.noreply.github.com> Date: Mon, 22 Sep 2025 20:26:07 +0200 Subject: [PATCH 1/2] Use correct term for "memoize" https://sr.wikipedia.org/sr-ec/%D0%9C%D0%B5%D0%BC%D0%BE%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%98%D0%B0 --- src/content/reference/react/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/reference/react/index.md b/src/content/reference/react/index.md index c7571d1a..29080993 100644 --- a/src/content/reference/react/index.md +++ b/src/content/reference/react/index.md @@ -31,7 +31,7 @@ React-dom sadrži funkcionalnosti koje su podržane samo za web aplikacije (koje ## React kompajler {/*react-compiler*/} -React kompajler je alat za optimizaciju vremena izgradnje koji automatski memoriše vaše React komponente i vrednosti: +React kompajler je alat za optimizaciju vremena izgradnje koji automatski memoizuje vaše React komponente i vrednosti: * [Konfiguracija](/reference/react-compiler/configuration) - Opcije za konfiguraciju React kompajlera. * [Direktive](/reference/react-compiler/directives) - Direktive na nivou funkcija za kontrolu kompilacije. From bb80565fd47969c0cf09434b70ee06660d9bbce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rado=C5=A1=20Mili=C4=87ev?= <40705899+rammba@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:40:32 +0200 Subject: [PATCH 2/2] Translate useCallback.md --- src/content/reference/react/useCallback.md | 270 ++++++++++----------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/src/content/reference/react/useCallback.md b/src/content/reference/react/useCallback.md index ed774d92..3b18db03 100644 --- a/src/content/reference/react/useCallback.md +++ b/src/content/reference/react/useCallback.md @@ -4,7 +4,7 @@ title: useCallback -`useCallback` is a React Hook that lets you cache a function definition between re-renders. +`useCallback` je React Hook koji vam omogućava da keširate definiciju funkcije između ponovnih rendera. ```js const cachedFn = useCallback(fn, dependencies) @@ -14,7 +14,7 @@ const cachedFn = useCallback(fn, dependencies) -[React Compiler](/learn/react-compiler) automatically memoizes values and functions, reducing the need for manual `useCallback` calls. You can use the compiler to handle memoization automatically. +[React kompajler](/learn/react-compiler) automatski memoizuje vrednosti i funkcije, što smanjuje potrebu za ručnim pozivima `useCallback`-a. Možete koristiti kompajler za automatsko rukovanje memoizacijom. @@ -26,7 +26,7 @@ const cachedFn = useCallback(fn, dependencies) ### `useCallback(fn, dependencies)` {/*usecallback*/} -Call `useCallback` at the top level of your component to cache a function definition between re-renders: +Pozovite `useCallback` na vrhu vaše komponente kako biste keširali definiciju funkcije između ponovnih rendera: ```js {4,9} import { useCallback } from 'react'; @@ -40,34 +40,34 @@ export default function ProductPage({ productId, referrer, theme }) { }, [productId, referrer]); ``` -[See more examples below.](#usage) +[Pogledajte još primera ispod.](#usage) -#### Parameters {/*parameters*/} +#### Parametri {/*parameters*/} -* `fn`: The function value that you want to cache. It can take any arguments and return any values. React will return (not call!) your function back to you during the initial render. On next renders, React will give you the same function again if the `dependencies` have not changed since the last render. Otherwise, it will give you the function that you have passed during the current render, and store it in case it can be reused later. React will not call your function. The function is returned to you so you can decide when and whether to call it. +* `fn`: Funkcija koju želite da keširate. Može primiti bilo koje argumente i vratiti bilo koje vrednosti. React će vratiti (ne pozvati!) vašu funkciju nazad tokom inicijalnog rendera. U narednim renderima, React će vam dati istu funkciju ako se `dependencies` nisu promenili od poslednjeg rendera. U suprotnom, vratiće vam funkciju koju ste prosledili tokom trenutnog rendera i sačuvati je za slučaj da se može iskoristiti kasnije. React neće pozvati vašu funkciju. Funkcija će vam biti vraćena kako biste odlučili kada i da li ćete je pozvati. -* `dependencies`: The list of all reactive values referenced inside of the `fn` code. Reactive values include props, state, and all the variables and functions declared directly inside your component body. If your linter is [configured for React](/learn/editor-setup#linting), it will verify that every reactive value is correctly specified as a dependency. The list of dependencies must have a constant number of items and be written inline like `[dep1, dep2, dep3]`. React will compare each dependency with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison algorithm. +* `dependencies`: Lista svih reaktivnih vrednosti referenciranih unutar koda `fn` funkcije. Reaktivne vrednosti uključuju props-e, state i sve promenljive i funkcije deklarisane direktno unutar tela vaše komponente. Ako vam je linter [konfigurisan za React](/learn/editor-setup#linting), verifikovaće da li je svaka reaktivna vrednost ispravno specificirana kao zavisnost. Lista zavisnosti mora imati konstantan broj članova i biti napisana inline poput `[dep1, dep2, dep3]`. React će uporediti svaku zavisnost sa njenom prethodnom vrednošću upotrebom [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) algoritma poređenja. -#### Returns {/*returns*/} +#### Povratne vrednosti {/*returns*/} -On the initial render, `useCallback` returns the `fn` function you have passed. +Prilikom inicijalnog rendera, `useCallback` vraća `fn` funkciju koju ste prosledili. -During subsequent renders, it will either return an already stored `fn` function from the last render (if the dependencies haven't changed), or return the `fn` function you have passed during this render. +Tokom narednih rendera, vratiće ili već sačuvanu `fn` funkciju iz prethodnog rendera (ako se zavisnosti nisu promenile), ili `fn` funkciju koju ste prosledili u trenutnom renderu. -#### Caveats {/*caveats*/} +#### Upozorenja {/*caveats*/} -* `useCallback` is a Hook, so you can only call it **at the top level of your component** or your own Hooks. You can't call it inside loops or conditions. If you need that, extract a new component and move the state into it. -* React **will not throw away the cached function unless there is a specific reason to do that.** For example, in development, React throws away the cache when you edit the file of your component. Both in development and in production, React will throw away the cache if your component suspends during the initial mount. In the future, React may add more features that take advantage of throwing away the cache--for example, if React adds built-in support for virtualized lists in the future, it would make sense to throw away the cache for items that scroll out of the virtualized table viewport. This should match your expectations if you rely on `useCallback` as a performance optimization. Otherwise, a [state variable](/reference/react/useState#im-trying-to-set-state-to-a-function-but-it-gets-called-instead) or a [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) may be more appropriate. +* `useCallback` je Hook, pa ga možete pozvati samo **na vrhu vaše komponente** ili vaših Hook-ova. Ne možete ga pozvati unutar petlji i uslova. Ako vam je to potrebno, izdvojite novu komponentu i pomerite state u nju. +* React **neće odbaciti keširanu funkciju osim ako ne postoji poseban razlog za tako nešto**. Na primer, u toku razvoja, React odbacuje keš kada izmenite fajl vaše komponente. U toku razvoja i u produkciji, React će odbaciti keš ako se vaša komponenta suspenduje tokom inicijalnog montiranja. U budućnosti, React može dodati nove funkcionalnosti koje koriste odbacivanje keša--na primer, ako React doda ugrađenu podršku za virtuelizovane liste u budućnosti, imalo bi smisla odbaciti keš za članove koji izlaze izvan vidnog polja virtuelizovane tabele. Ovo bi trebalo ispuniti vaša očekivanja ako se uzdate u `useCallback` za optimizaciju performansi. Inače, [state promenljiva](/reference/react/useState#im-trying-to-set-state-to-a-function-but-it-gets-called-instead) ili [ref](/reference/react/useRef#avoiding-recreating-the-ref-contents) mogu biti prikladnija rešenja. --- -## Usage {/*usage*/} +## Upotreba {/*usage*/} -### Skipping re-rendering of components {/*skipping-re-rendering-of-components*/} +### Preskakanje ponovnog renderovanja komponenata {/*skipping-re-rendering-of-components*/} -When you optimize rendering performance, you will sometimes need to cache the functions that you pass to child components. Let's first look at the syntax for how to do this, and then see in which cases it's useful. +Kada optimizujete performanse renderovanja, ponekad ćete trebati da keširate funkcije koje prosleđujete dečjim komponentama. Hajde prvo da pogledamo sintaksu kako bi se to moglo uraditi i da vidimo u kojim slučajevima je to korisno. -To cache a function between re-renders of your component, wrap its definition into the `useCallback` Hook: +Da biste keširali funkciju između ponovnih rendera vaše komponente, obmotajte njenu definiciju sa `useCallback` Hook-om: ```js [[3, 4, "handleSubmit"], [2, 9, "[productId, referrer]"]] import { useCallback } from 'react'; @@ -82,20 +82,20 @@ function ProductPage({ productId, referrer, theme }) { // ... ``` -You need to pass two things to `useCallback`: +Potrebno je proslediti dve stvari u `useCallback`: -1. A function definition that you want to cache between re-renders. -2. A list of dependencies including every value within your component that's used inside your function. +1. Definiciju funkcije koju želite keširati između ponovnih rendera. +2. Listu zavisnosti koja uključuje svaku vrednost unutar vaše komponente koja se koristi unutar te funkcije. -On the initial render, the returned function you'll get from `useCallback` will be the function you passed. +Prilikom inicijalnog rendera, povratna funkcija koju dobijate iz `useCallback`-a će biti funkcija koju ste prosledili. -On the following renders, React will compare the dependencies with the dependencies you passed during the previous render. If none of the dependencies have changed (compared with [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useCallback` will return the same function as before. Otherwise, `useCallback` will return the function you passed on *this* render. +U narednim renderima, React će porediti zavisnosti sa zavisnostima koje ste prosledili tokom prethodnog rendera. Ako se nijedna zavisnost nije promenila (poređenjem sa [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), `useCallback` će vratiti istu funkciju kao i pre. U suprotnom, `useCallback` će vratiti funkciju koju ste prosledili u *ovom* renderu. -In other words, `useCallback` caches a function between re-renders until its dependencies change. +Drugim rečima, `useCallback` kešira funkciju između ponovnih rendera dok joj se zavisnosti ne promene. -**Let's walk through an example to see when this is useful.** +**Prođimo kroz primer da vidimo kada je ovo korisno.** -Say you're passing a `handleSubmit` function down from the `ProductPage` to the `ShippingForm` component: +Recimo da prosleđujete `handleSubmit` funkciju iz `ProductPage` u `ShippingForm` komponentu: ```js {5} function ProductPage({ productId, referrer, theme }) { @@ -107,9 +107,9 @@ function ProductPage({ productId, referrer, theme }) { ); ``` -You've noticed that toggling the `theme` prop freezes the app for a moment, but if you remove `` from your JSX, it feels fast. This tells you that it's worth trying to optimize the `ShippingForm` component. +Primetili ste da promena `theme` prop-a zamrzava aplikaciju na sekund, ali ako uklonite `` iz JSX-a, sve radi brzo. Ovo vam govori da nije loše probati da optimizujete `ShippingForm` komponentu. -**By default, when a component re-renders, React re-renders all of its children recursively.** This is why, when `ProductPage` re-renders with a different `theme`, the `ShippingForm` component *also* re-renders. This is fine for components that don't require much calculation to re-render. But if you verified a re-render is slow, you can tell `ShippingForm` to skip re-rendering when its props are the same as on last render by wrapping it in [`memo`:](/reference/react/memo) +**Po default-u, kada se komponenta ponovo renderuje, React rekurzivno ponovo renderuje svu njenu decu.** Zbog toga, kada se `ProductPage` ponovo renderuje sa novom `theme`, i `ShippingForm` komponenta se *takođe* ponovo renderuje. Ovo je u redu za komponente koje ne zahtevaju mnogo proračuna za ponovno renderovanje. Ali, ako ste potvrdili da je ponovno renderovanje sporo, možete reći `ShippingForm` komponenti da preskoči renderovanje kada su njeni props-i isti kao i u prethodnom renderu, tako što ćete je obmotati sa [`memo`](/reference/react/memo): ```js {3,5} import { memo } from 'react'; @@ -119,11 +119,11 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { }); ``` -**With this change, `ShippingForm` will skip re-rendering if all of its props are the *same* as on the last render.** This is when caching a function becomes important! Let's say you defined `handleSubmit` without `useCallback`: +**Sa ovom promenom, `ShippingForm` će preskočiti ponovno renderovanje ako su joj svi props-i *isti* kao i u poslednjem renderu.** Ovde keširanje funkcije postaje važno! Recimo da ste definisali `handleSubmit` bez `useCallback`: ```js {2,3,8,12-13} function ProductPage({ productId, referrer, theme }) { - // Every time the theme changes, this will be a different function... + // Svaki put kad se theme promeni, ovo će biti drugačija funkcija... function handleSubmit(orderDetails) { post('/product/' + productId + '/buy', { referrer, @@ -133,47 +133,47 @@ function ProductPage({ productId, referrer, theme }) { return (
- {/* ... so ShippingForm's props will never be the same, and it will re-render every time */} + {/* ... pa props-i ShippingForm-a nikad neće biti isti i svaki put će se ponovo renderovati */}
); } ``` -**In JavaScript, a `function () {}` or `() => {}` always creates a _different_ function,** similar to how the `{}` object literal always creates a new object. Normally, this wouldn't be a problem, but it means that `ShippingForm` props will never be the same, and your [`memo`](/reference/react/memo) optimization won't work. This is where `useCallback` comes in handy: +**U JavaScript-u, `function () {}` ili `() => {}` uvek kreira _drugačiju_ funkciju**, slično kao što `{}` literal objekta uvek kreira novi objekat. Obično ovo ne bi bio problem, ali ovo znači da props-i `ShippingForm`-a nikad neće biti isti i da vaša [`memo`](/reference/react/memo) optimizacija ne radi. Ovde `useCallback` postaje korisna: ```js {2,3,8,12-13} function ProductPage({ productId, referrer, theme }) { - // Tell React to cache your function between re-renders... + // Reci React-u da kešira tvoju funkciju između ponovnih rendera... const handleSubmit = useCallback((orderDetails) => { post('/product/' + productId + '/buy', { referrer, orderDetails, }); - }, [productId, referrer]); // ...so as long as these dependencies don't change... + }, [productId, referrer]); // ...i dok god se ove zavisnosti ne menjaju... return (
- {/* ...ShippingForm will receive the same props and can skip re-rendering */} + {/* ...ShippingForm će primiti iste props-e i preskočiti ponovni render */}
); } ``` -**By wrapping `handleSubmit` in `useCallback`, you ensure that it's the *same* function between the re-renders** (until dependencies change). You don't *have to* wrap a function in `useCallback` unless you do it for some specific reason. In this example, the reason is that you pass it to a component wrapped in [`memo`,](/reference/react/memo) and this lets it skip re-rendering. There are other reasons you might need `useCallback` which are described further on this page. +**Obmotavanjem `handleSubmit`-a sa `useCallback` osiguravate da je to *ista* funkcija između ponovnih rendera** (dok se zavisnosti ne promene). Ne *trebate da* obmotate funkciju sa `useCallback` ako nemate poseban razlog. U ovom primeru, razlog je to što je prosleđujete u komponentu obmotanu sa [`memo`](/reference/react/memo), pa vam to omogućava da preskočite ponovno renderovanje. Postoje i drugi razlozi za upotrebu `useCallback`-a koji su opisani kasnije na ovoj stranici. -**You should only rely on `useCallback` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `useCallback` back. +**Treba se oslanjati na `useCallback` samo kao optimizaciju performansi.** Ako vaš kod ne radi bez toga, pronađite glavni razlog i prvo ga popravite. Tek onda možete dodati `useCallback`. -#### How is useCallback related to useMemo? {/*how-is-usecallback-related-to-usememo*/} +#### Kako je useCallback povezan sa useMemo? {/*how-is-usecallback-related-to-usememo*/} -You will often see [`useMemo`](/reference/react/useMemo) alongside `useCallback`. They are both useful when you're trying to optimize a child component. They let you [memoize](https://en.wikipedia.org/wiki/Memoization) (or, in other words, cache) something you're passing down: +Često ćete videti [`useMemo`](/reference/react/useMemo) pored `useCallback`-a. Oba su korisna kada pokušavate optimizovati dečju komponentu. Omogućavaju vam da [memoizujete](https://sr.wikipedia.org/sr-ec/%D0%9C%D0%B5%D0%BC%D0%BE%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%98%D0%B0) (ili, drugim rečima, keširate) nešto što prosleđujete deci: ```js {6-8,10-15,19} import { useMemo, useCallback } from 'react'; @@ -181,11 +181,11 @@ import { useMemo, useCallback } from 'react'; function ProductPage({ productId, referrer }) { const product = useData('/product/' + productId); - const requirements = useMemo(() => { // Calls your function and caches its result + const requirements = useMemo(() => { // Poziva vašu funkciju i kešira rezultat return computeRequirements(product); }, [product]); - const handleSubmit = useCallback((orderDetails) => { // Caches your function itself + const handleSubmit = useCallback((orderDetails) => { // Kešira samu funkciju post('/product/' + productId + '/buy', { referrer, orderDetails, @@ -200,60 +200,60 @@ function ProductPage({ productId, referrer }) { } ``` -The difference is in *what* they're letting you cache: +Razlika je u tome *šta* vam omogućavaju da keširate: -* **[`useMemo`](/reference/react/useMemo) caches the *result* of calling your function.** In this example, it caches the result of calling `computeRequirements(product)` so that it doesn't change unless `product` has changed. This lets you pass the `requirements` object down without unnecessarily re-rendering `ShippingForm`. When necessary, React will call the function you've passed during rendering to calculate the result. -* **`useCallback` caches *the function itself.*** Unlike `useMemo`, it does not call the function you provide. Instead, it caches the function you provided so that `handleSubmit` *itself* doesn't change unless `productId` or `referrer` has changed. This lets you pass the `handleSubmit` function down without unnecessarily re-rendering `ShippingForm`. Your code won't run until the user submits the form. +* **[`useMemo`](/reference/react/useMemo) kešira *rezultat* poziva vaše funkcije.** U ovom primeru, kešira rezultat poziva `computeRequirements(product)` tako da se ne menja dok se `product` ne promeni. Ovo vam omogućava da prosledite `requirements` objekat bez nepotrebnih ponovnih rendera `ShippingForm`-a. Kada je neophodno, React će tokom renderovanja pozvati funkciju koju ste prosledili kako bi izračunao rezultat. +* **`useCallback` kešira *samu funkciju***. Za razliku od `useMemo`-a, ne poziva funkciju koju prosledite. Umesto toga, kešira prosleđenu funkciju tako da se *sama* `handleSubmit` ne menja osim ako se `productId` ili `referrer` promene. Ovo vam omogućava da prosledite `handleSubmit` funkciju bez nepotrebnih ponovnih rendera `ShippingForm`-a. Vaš kod se neće izvršiti dok korisnik ne submit-uje formu. -If you're already familiar with [`useMemo`,](/reference/react/useMemo) you might find it helpful to think of `useCallback` as this: +Ako ste već upoznati sa [`useMemo`](/reference/react/useMemo), može vam biti lakše da razmišljate o `useCallback`-u na ovaj način: ```js {expectedErrors: {'react-compiler': [3]}} -// Simplified implementation (inside React) +// Pojednostavljena implementacija (unutar React-a) function useCallback(fn, dependencies) { return useMemo(() => fn, dependencies); } ``` -[Read more about the difference between `useMemo` and `useCallback`.](/reference/react/useMemo#memoizing-a-function) +[Čitajte još o razlici između `useMemo` i `useCallback`.](/reference/react/useMemo#memoizing-a-function) -#### Should you add useCallback everywhere? {/*should-you-add-usecallback-everywhere*/} +#### Trebate li dodati useCallback svuda? {/*should-you-add-usecallback-everywhere*/} -If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. +Ako je vaša aplikacija poput ovog sajta, gde su većinom grube interakcije (poput zamene stranice ili cele sekcije), memoizacija uglavnom nije potrebna. Na drugu stranu, ako vam aplikacija liči na editor crteža i interakcije su uglavnom granularne (poput pomeranja oblika), onda vam memoizacija može biti od velike pomoći. -Caching a function with `useCallback` is only valuable in a few cases: +Keširanje funkcije sa `useCallback` je korisno samo u par slučajeva: -- You pass it as a prop to a component wrapped in [`memo`.](/reference/react/memo) You want to skip re-rendering if the value hasn't changed. Memoization lets your component re-render only if dependencies changed. -- The function you're passing is later used as a dependency of some Hook. For example, another function wrapped in `useCallback` depends on it, or you depend on this function from [`useEffect.`](/reference/react/useEffect) +- Prosleđujete je kao prop u komponentu koja je obmotana sa [`memo`](/reference/react/memo). Želite preskočiti ponovno renderovanje ako se vrednost nije promenila. Memoizacija čini da se vaša komponenta ponovo renderuje samo ako se zavisnosti promene. +- Funkcija koju prosleđujete se kasnije koristi kao zavisnost nekog Hook-a. Na primer, druga funkcija obmotana sa `useCallback` zavisi od nje, ili zavisite od te funkcije kroz [`useEffect`](/reference/react/useEffect). -There is no benefit to wrapping a function in `useCallback` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component. +Nema benefita obmotavati funkciju sa `useCallback` u ostalim slučajevima. Doduše, ne postoji ni značajna šteta u tome, pa neki timovi odlučuju da ne razmišljaju o pojedinačnim slučajevima i da memoizuju što je više moguće. Loša strana je da kod postaje manje čitljiv. Takođe, nije svaka memoizacija efikasna: pojedinačna vrednost koja je "uvek nova" je dovoljna da slomi memoizaciju za celu komponentu. -Note that `useCallback` does not prevent *creating* the function. You're always creating a function (and that's fine!), but React ignores it and gives you back a cached function if nothing changed. +Primetite da `useCallback` ne sprečava *kreiranje* funkcije. Uvek kreirate funkciju (i to je u redu!), ali React to ignoriše i vraća vam keširanu funkciju ako se ništa nije promenilo. -**In practice, you can make a lot of memoization unnecessary by following a few principles:** +**U praksi dosta memoizacije možete učiniti nepotrebnom ako pratite par principa:** -1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) Then, if the wrapper component updates its own state, React knows that its children don't need to re-render. -1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. Don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library. -1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization. -1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over. -1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component. +1. Kada komponenta vizuelno obmotava druge komponente, napravite da [prima JSX kao decu](/learn/passing-props-to-a-component#passing-jsx-as-children). U tom slučaju, ako obmotavajuća komponenta ažurira svoj state, React zna da njena deca ne trebaju ponovo da se renderuju. +2. Koristite lokalni state i nemojte [podizati state](/learn/sharing-state-between-components) više nego što je potrebno. Nemojte čuvati prolazni state poput formi i podataka da li prelazite mišem preko nečega na vrhu stabla ili u globalnoj state biblioteci. +3. Postarajte se da je [logika renderovanja čista](/learn/keeping-components-pure). Ako ponovni render komponente pravi problem ili neki uočljivi vizuelni artefakt, to je bug u komponenti! Popravite bug umesto dodavanja memoizacije. +4. Izbegavajte [nepotrebne Effect-e koji ažuriraju state](/learn/you-might-not-need-an-effect). Većina problema sa performansama u React aplikacijama prouzrokovani su nizom ažuriranja koji potiču od Effect-a koji iznova i iznova renderuju vaše komponente. +5. Pokušajte da [uklonite nepotrebne zavisnosti u Effect-ima](/learn/removing-effect-dependencies). Na primer, umesto memoizacije, često je lakše pomeriti neki objekat ili funkciju unutar Effect-a ili izvan komponente. -If a specific interaction still feels laggy, [use the React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to see which components benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In long term, we're researching [doing memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all. +Ako neka posebna interakcija i dalje deluje da lag-uje, [iskoristite React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) da vidite koje komponente mogu imati benefita od memoizacije i dodajte memoizaciju gde je potrebno. Ovi principi čine vaše komponente lakšim za debug-ovanje i razumevanje, pa je dobro da ih uvek pratite. Dugoročno, mi istražujemo [automatsku upotrebu memoizacije](https://www.youtube.com/watch?v=lGEMwh32soc) da ovo rešimo jednom za svagda. - + -#### Skipping re-rendering with `useCallback` and `memo` {/*skipping-re-rendering-with-usecallback-and-memo*/} +#### Preskakanje ponovnog renderovanja sa `useCallback` i `memo` {/*skipping-re-rendering-with-usecallback-and-memo*/} -In this example, the `ShippingForm` component is **artificially slowed down** so that you can see what happens when a React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. +U ovom primeru, `ShippingForm` komponenta je **veštački usporena** kako biste mogli videti šta se dešava kada je React komponenta koju renderujete zapravo spora. Pokušajte inkrementirati brojač i menjati temu. -Incrementing the counter feels slow because it forces the slowed down `ShippingForm` to re-render. That's expected because the counter has changed, and so you need to reflect the user's new choice on the screen. +Inkrementiranje brojača deluje sporo jer tera usporenu `ShippingForm` komponentu da se ponovo renderuje. To je očekivano jer se brojač promenio, pa morate da prikažete korisnikov novi izbor na ekranu. -Next, try toggling the theme. **Thanks to `useCallback` together with [`memo`](/reference/react/memo), it’s fast despite the artificial slowdown!** `ShippingForm` skipped re-rendering because the `handleSubmit` function has not changed. The `handleSubmit` function has not changed because both `productId` and `referrer` (your `useCallback` dependencies) haven't changed since last render. +Posle toga probajte da promenite temu. **Zahvaljujući `useCallback`-u zajedno sa [`memo`](/reference/react/memo), promena je brza uprkos veštačkom usporavanju!** `ShippingForm` je preskočila ponovno renderovanje jer se `handleSubmit` funkcija nije promenila. `handleSubmit` funkcija se nije promenila jer se ni `productId` ni `referrer` (zavisnosti `useCallback`-a) nisu promenili od poslednjeg rendera. @@ -271,7 +271,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Dark mode + Tamni režim
'); + console.log('[VEŠTAČKI SPORO] Renderovanje '); let startTime = performance.now(); while (performance.now() - startTime < 500) { - // Do nothing for 500 ms to emulate extremely slow code + // Ništa ne radi 500 ms da simuliraš veoma spor kod } function handleSubmit(e) { @@ -334,23 +334,23 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { return (
-

Note: ShippingForm is artificially slowed down!

+

Napomena: ShippingForm je veštački usporena!

@@ -389,11 +389,11 @@ button[type="button"] { -#### Always re-rendering a component {/*always-re-rendering-a-component*/} +#### Uvek ponovno renderovanje komponente {/*always-re-rendering-a-component*/} -In this example, the `ShippingForm` implementation is also **artificially slowed down** so that you can see what happens when some React component you're rendering is genuinely slow. Try incrementing the counter and toggling the theme. +U ovom primeru, `ShippingForm` komponenta je takođe **veštački usporena** kako biste mogli videti šta se dešava kada je React komponenta koju renderujete zapravo spora. Pokušajte inkrementirati brojač i menjati temu. -Unlike in the previous example, toggling the theme is also slow now! This is because **there is no `useCallback` call in this version,** so `handleSubmit` is always a new function, and the slowed down `ShippingForm` component can't skip re-rendering. +Za razliku od prethodnog primera, sada je promena teme takođe spora! To se dešava jer **nema `useCallback` poziva u ovoj verziji**, pa je `handleSubmit` uvek nova funkcija i usporena `ShippingForm` komponenta ne može da preskoči ponovno renderovanje. @@ -411,7 +411,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Dark mode + Tamni režim
'); + console.log('[VEŠTAČKI SPORO] Renderovanje '); let startTime = performance.now(); while (performance.now() - startTime < 500) { - // Do nothing for 500 ms to emulate extremely slow code + // Ništa ne radi 500 ms da simuliraš veoma spor kod } function handleSubmit(e) { @@ -473,23 +473,23 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { return ( -

Note: ShippingForm is artificially slowed down!

+

Napomena: ShippingForm je veštački usporena!

@@ -527,7 +527,7 @@ button[type="button"] {
-However, here is the same code **with the artificial slowdown removed.** Does the lack of `useCallback` feel noticeable or not? +Međutim, evo istog koda **gde je veštačko usporavanje uklonjeno**. Da li se nedostatak `useCallback`-a oseća ili ne? @@ -545,7 +545,7 @@ export default function App() { checked={isDark} onChange={e => setIsDark(e.target.checked)} /> - Dark mode + Tamni režim
'); + console.log('Renderovanje '); function handleSubmit(e) { e.preventDefault(); @@ -604,21 +604,21 @@ const ShippingForm = memo(function ShippingForm({ onSubmit }) { return ( @@ -656,9 +656,9 @@ button[type="button"] {
-Quite often, code without memoization works fine. If your interactions are fast enough, you don't need memoization. +Veoma često kod bez memoizacije radi dobro. Ako su vaše interakcije dovoljno brze, ne treba vam memoizacija. -Keep in mind that you need to run React in production mode, disable [React Developer Tools](/learn/react-developer-tools), and use devices similar to the ones your app's users have in order to get a realistic sense of what's actually slowing down your app. +Imajte na umu da morate pokrenuti React u produkciji, onemogućiti [React Developer Tools](/learn/react-developer-tools) i koristiti uređaje slične onima koje koriste korisnici vaše aplikacije kako biste imali realističnu sliku šta zapravo usporava vašu aplikaciju. @@ -666,11 +666,11 @@ Keep in mind that you need to run React in production mode, disable [React Devel --- -### Updating state from a memoized callback {/*updating-state-from-a-memoized-callback*/} +### Ažuriranje state-a iz memoizovanog callback-a {/*updating-state-from-a-memoized-callback*/} -Sometimes, you might need to update state based on previous state from a memoized callback. +Ponekad će vam trebati da ažurirate state na osnovu prethodnog state-a u memoizovanom callback-u. -This `handleAddTodo` function specifies `todos` as a dependency because it computes the next todos from it: +Ova `handleAddTodo` funkcija specificira `todos` kao zavisnost jer na osnovu nje računa naredni todos: ```js {6,7} function TodoList() { @@ -683,7 +683,7 @@ function TodoList() { // ... ``` -You'll usually want memoized functions to have as few dependencies as possible. When you read some state only to calculate the next state, you can remove that dependency by passing an [updater function](/reference/react/useState#updating-state-based-on-the-previous-state) instead: +Uglavnom želite da memoizovane funkcije imaju što manje zavisnosti. Kada neki state čitate samo da biste izračunali naredni state, možete ukloniti tu zavisnost prosleđivanjem [updater funkcije](/reference/react/useState#updating-state-based-on-the-previous-state): ```js {6,7} function TodoList() { @@ -692,17 +692,17 @@ function TodoList() { const handleAddTodo = useCallback((text) => { const newTodo = { id: nextId++, text }; setTodos(todos => [...todos, newTodo]); - }, []); // ✅ No need for the todos dependency + }, []); // ✅ Nema potrebe za todos zavisnošću // ... ``` -Here, instead of making `todos` a dependency and reading it inside, you pass an instruction about *how* to update the state (`todos => [...todos, newTodo]`) to React. [Read more about updater functions.](/reference/react/useState#updating-state-based-on-the-previous-state) +Ovde, umesto da imate `todos` zavisnost i da je čitate, React-u prosleđujete instrukciju *kako* ažurirati state (`todos => [...todos, newTodo]`). [Pročitajte još o updater funkcijama.](/reference/react/useState#updating-state-based-on-the-previous-state) --- -### Preventing an Effect from firing too often {/*preventing-an-effect-from-firing-too-often*/} +### Sprečavanje prečestog okidanja Effect-a {/*preventing-an-effect-from-firing-too-often*/} -Sometimes, you might want to call a function from inside an [Effect:](/learn/synchronizing-with-effects) +Ponekad vam može biti potrebno da pozovete funkciju unutar [Effect-a](/learn/synchronizing-with-effects): ```js {4-9,12} function ChatRoom({ roomId }) { @@ -722,7 +722,7 @@ function ChatRoom({ roomId }) { // ... ``` -This creates a problem. [Every reactive value must be declared as a dependency of your Effect.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) However, if you declare `createOptions` as a dependency, it will cause your Effect to constantly reconnect to the chat room: +Ovo stvara problem. [Svaka reaktivna vrednost mora biti deklarisana kao zavisnost vašeg Effect-a.](/learn/lifecycle-of-reactive-effects#react-verifies-that-you-specified-every-reactive-value-as-a-dependency) Međutim, ako deklarišete `createOptions` kao zavisnost, to će uticati da se vaš Effect konstantno rekonektuje na sobu za dopisivanje: ```js {6} @@ -731,11 +731,11 @@ This creates a problem. [Every reactive value must be declared as a dependency o const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // 🔴 Problem: This dependency changes on every render + }, [createOptions]); // 🔴 Problem: Ova zavisnost se menja svakim renderom // ... ``` -To solve this, you can wrap the function you need to call from an Effect into `useCallback`: +Da biste ovo rešili, možete obmotati funkciju koju trebate pozvati unutar Effect-a sa `useCallback`: ```js {4-9,16} function ChatRoom({ roomId }) { @@ -746,25 +746,25 @@ function ChatRoom({ roomId }) { serverUrl: 'https://localhost:1234', roomId: roomId }; - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Menja se samo kada se roomId promeni useEffect(() => { const options = createOptions(); const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [createOptions]); // ✅ Only changes when createOptions changes + }, [createOptions]); // ✅ Menja se samo kada se createOptions promeni // ... ``` -This ensures that the `createOptions` function is the same between re-renders if the `roomId` is the same. **However, it's even better to remove the need for a function dependency.** Move your function *inside* the Effect: +Ovo osigurava da je `createOptions` funkcija ista između ponovnih rendera ako je `roomId` isto. **Međutim, još je bolje ukloniti potrebu da imate funkciju kao zavisnost.** Pomerite funkciju *unutar* Effect-a: ```js {5-10,16} function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { - function createOptions() { // ✅ No need for useCallback or function dependencies! + function createOptions() { // ✅ Nema potrebe za useCallback ili funkcijama kao zavisnostima! return { serverUrl: 'https://localhost:1234', roomId: roomId @@ -775,17 +775,17 @@ function ChatRoom({ roomId }) { const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); - }, [roomId]); // ✅ Only changes when roomId changes + }, [roomId]); // ✅ Menja se samo kada se roomId promeni // ... ``` -Now your code is simpler and doesn't need `useCallback`. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +Sada je vaš kod jednostavniji i nema potrebu za `useCallback`. [Naučite više o uklanjanju Effect zavisnosti.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) --- -### Optimizing a custom Hook {/*optimizing-a-custom-hook*/} +### Optimizovanje prilagođenog Hook-a {/*optimizing-a-custom-hook*/} -If you're writing a [custom Hook,](/learn/reusing-logic-with-custom-hooks) it's recommended to wrap any functions that it returns into `useCallback`: +Ako pišete [prilagođeni Hook](/learn/reusing-logic-with-custom-hooks), preporučujemo da sve funkcije koje on vraća obmotate sa `useCallback`: ```js {4-6,8-10} function useRouter() { @@ -806,17 +806,17 @@ function useRouter() { } ``` -This ensures that the consumers of your Hook can optimize their own code when needed. +Ovo osigurava da korisnici vašeg Hook-a mogu optimizovati svoj kod kada bude potrebno. --- -## Troubleshooting {/*troubleshooting*/} +## Rešavanje problema {/*troubleshooting*/} -### Every time my component renders, `useCallback` returns a different function {/*every-time-my-component-renders-usecallback-returns-a-different-function*/} +### Svaki put kad se moja komponenta renderuje, `useCallback` vraća drugačiju funkciju {/*every-time-my-component-renders-usecallback-returns-a-different-function*/} -Make sure you've specified the dependency array as a second argument! +Postarajte se da specificirate niz zavisnosti kao drugi argument! -If you forget the dependency array, `useCallback` will return a new function every time: +Ako zaboravite niz zavisnosti, `useCallback` će vratiti novu funkciju svaki put: ```js {7} function ProductPage({ productId, referrer }) { @@ -825,11 +825,11 @@ function ProductPage({ productId, referrer }) { referrer, orderDetails, }); - }); // 🔴 Returns a new function every time: no dependency array + }); // 🔴 Vraća novu funkciju svaki put: nema niza zavisnosti // ... ``` -This is the corrected version passing the dependency array as a second argument: +Ovo je ispravljena verzija gde se prosleđuje niz zavisnosti kao drugi argument: ```js {7} function ProductPage({ productId, referrer }) { @@ -838,11 +838,11 @@ function ProductPage({ productId, referrer }) { referrer, orderDetails, }); - }, [productId, referrer]); // ✅ Does not return a new function unnecessarily + }, [productId, referrer]); // ✅ Ne vraća novu funkciju bespotrebno // ... ``` -If this doesn't help, then the problem is that at least one of your dependencies is different from the previous render. You can debug this problem by manually logging your dependencies to the console: +Ako vam ovo ne pomaže, problem je u tome da je barem jedna od vaših zavisnosti različita od prethodnog rendera. Možete debug-ovati ovaj problem ručnim logovanjem vaših zavisnosti u konzolu: ```js {5} const handleSubmit = useCallback((orderDetails) => { @@ -852,28 +852,28 @@ If this doesn't help, then the problem is that at least one of your dependencies console.log([productId, referrer]); ``` -You can then right-click on the arrays from different re-renders in the console and select "Store as a global variable" for both of them. Assuming the first one got saved as `temp1` and the second one got saved as `temp2`, you can then use the browser console to check whether each dependency in both arrays is the same: +Zatim možete upotrebiti desni klik na nizove iz različitih rendera u konzoli i izabrati "Store as a global variable" za oba. Pretpostavkom da je prvi sačuvan kao `temp1`, a drugi kao `temp2`, možete upotrebiti konzolu pretraživača da proverite da li je svaka od zavisnosti u oba niza jednaka: ```js -Object.is(temp1[0], temp2[0]); // Is the first dependency the same between the arrays? -Object.is(temp1[1], temp2[1]); // Is the second dependency the same between the arrays? -Object.is(temp1[2], temp2[2]); // ... and so on for every dependency ... +Object.is(temp1[0], temp2[0]); // Da li je prva zavisnost jednaka u oba niza? +Object.is(temp1[1], temp2[1]); // Da li je druga zavisnost jednaka u oba niza? +Object.is(temp1[2], temp2[2]); // ... i tako dalje za svaku zavisnost ... ``` -When you find which dependency is breaking memoization, either find a way to remove it, or [memoize it as well.](/reference/react/useMemo#memoizing-a-dependency-of-another-hook) +Kada pronađete koja zavisnost kvari memoizaciju, pronađite način da je uklonite ili [je takođe memoizujte](/reference/react/useMemo#memoizing-a-dependency-of-another-hook). --- -### I need to call `useCallback` for each list item in a loop, but it's not allowed {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} +### Trebam pozvati `useCallback` za svaki član niza u petlji, ali nije dozvoljeno {/*i-need-to-call-usememo-for-each-list-item-in-a-loop-but-its-not-allowed*/} -Suppose the `Chart` component is wrapped in [`memo`](/reference/react/memo). You want to skip re-rendering every `Chart` in the list when the `ReportList` component re-renders. However, you can't call `useCallback` in a loop: +Pretpostavimo da je `Chart` komponenta obmotana sa [`memo`](/reference/react/memo). Želite da preskočite ponovno renderovanje svakog `Chart`-a u listi kada se `ReportList` komponenta ponovo renderuje. Međutim, ne možete pozvati `useCallback` u petlji: ```js {expectedErrors: {'react-compiler': [6]}} {5-14} function ReportList({ items }) { return (
{items.map(item => { - // 🔴 You can't call useCallback in a loop like this: + // 🔴 Ne možeš pozvati useCallback u petlji ovako: const handleClick = useCallback(() => { sendReport(item) }, [item]); @@ -889,7 +889,7 @@ function ReportList({ items }) { } ``` -Instead, extract a component for an individual item, and put `useCallback` there: +Umesto toga, izdvojite komponentu za posebnu stavku i tamo stavite `useCallback`: ```js {5,12-21} function ReportList({ items }) { @@ -903,7 +903,7 @@ function ReportList({ items }) { } function Report({ item }) { - // ✅ Call useCallback at the top level: + // ✅ Pozovi useCallback na vrhu komponente: const handleClick = useCallback(() => { sendReport(item) }, [item]); @@ -916,7 +916,7 @@ function Report({ item }) { } ``` -Alternatively, you could remove `useCallback` in the last snippet and instead wrap `Report` itself in [`memo`.](/reference/react/memo) If the `item` prop does not change, `Report` will skip re-rendering, so `Chart` will skip re-rendering too: +Alternativno, možete ukloniti `useCallback` iz poslednjeg snippet-a i umesto toga obmotati `Report` sa [`memo`](/reference/react/memo). Ako se `item` prop ne promeni, `Report` će preskočiti ponovni render, što znači da će i `Chart` preskočiti ponovni render: ```js {5,6-8,15} function ReportList({ items }) {