অনুশীলনে ব্যবহারকালব্যাক এবং ব্যবহারমেমোর মধ্যে পার্থক্য কী?


89

হতে পারে আমি কিছু ভুল বুঝেছি, তবে রি-রেন্ডার হওয়ার পরে ইউজক্যালব্যাক হুক প্রতিবার চলে।

আমি ইনপুটগুলি পাস করলাম - কলব্যাক ব্যবহারের দ্বিতীয় যুক্তি হিসাবে - অ-সর্বদা-পরিবর্তনযোগ্য ধ্রুবক - তবে ফিরে আসা মেমোজাইজড কলব্যাক এখনও প্রতিটি রেন্ডারে আমার ব্যয়বহুল গণনা চালায় (আমি বেশ নিশ্চিত - আপনি নীচের স্নিপেটে নিজের দ্বারা পরীক্ষা করতে পারেন)।

আমি মেমো ব্যবহারের জন্য কলব্যাকটি পরিবর্তন করেছি - এবং ব্যবহারমেমো প্রত্যাশা অনুযায়ী কাজ করে - পাসের ইনপুট পরিবর্তনের পরে চলে। এবং সত্যিই ব্যয়বহুল গণনা স্মরণ করে।

সরাসরি উদাহরণ:

'use strict';

const { useState, useCallback, useMemo } = React;

const neverChange = 'I never change';
const oneSecond = 1000;

function App() {
  const [second, setSecond] = useState(0);
  
  // This 👇 expensive function executes everytime when render happens:
  const calcCallback = useCallback(() => expensiveCalc('useCallback'), [neverChange]);
  const computedCallback = calcCallback();
  
  // This 👇 executes once
  const computedMemo = useMemo(() => expensiveCalc('useMemo'), [neverChange]);
  
  setTimeout(() => setSecond(second + 1), oneSecond);
  
  return `
    useCallback: ${computedCallback} times |
    useMemo: ${computedMemo} |
    App lifetime: ${second}sec.
  `;
}

const tenThousand = 10 * 1000;
let expensiveCalcExecutedTimes = { 'useCallback': 0, 'useMemo': 0 };

function expensiveCalc(hook) {
  let i = 0;
  while (i < tenThousand) i++;
  
  return ++expensiveCalcExecutedTimes[hook];
}


ReactDOM.render(
  React.createElement(App),
  document.querySelector('#app')
);
<h1>useCallback vs useMemo:</h1>
<div id="app">Loading...</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>


4
আমি আপনাকে কল করার প্রয়োজন মনে করি না computedCallback = calcCallback();computedCallbackশুধু = ক্যালক্যালব্যাক , it will update the callback once কখনই পরিবর্তন হওয়া উচিত ।
নোটিডার্ট

4
ইউজক্যালব্যাক (এফএন, ডিপস) ইউজেমো (())> ফেন, ডিপস) এর সমতুল্য।
হেনরি লিউ

উত্তর:


155

টিএল; ডিআর;

  • useMemo একটি ফাংশন এর কল এবং রেন্ডার মধ্যে একটি গণনা ফলাফল স্মরণ করা হয়
  • useCallback রেন্ডারগুলির মধ্যে একটি কলব্যাক নিজেই স্মরণ করা (রেফারেনশিয়াল সমতা)
  • useRef রেন্ডারগুলির মধ্যে ডেটা রাখা (আপডেট করা পুনরায় উপস্থাপনে আগুন দেয় না)
  • useState রেন্ডারগুলির মধ্যে ডেটা রাখা (আপডেট করা পুনরায় রেন্ডারিংয়ে আগুন জ্বালিয়ে দেবে)

দীর্ঘ সংস্করণ:

useMemo ভারী গণনা এড়ানো উপর দৃষ্টি নিবদ্ধ করে।

useCallbackএকটি ভিন্ন বিষয়কে কেন্দ্র করে: যখন ইনলাইন ইভেন্ট হ্যান্ডলারের onClick={() => { doSomething(...); }কারণ PureComponentশিশুদের পুনরায় রেন্ডারিংয়ের মতো কার্য সম্পাদন সংক্রান্ত সমস্যাগুলি ঠিক করে (কারণ প্রতিটি সময় ফাংশন এক্সপ্রেশনগুলি আলাদাভাবে দেখা যায়)

এই কথা বলে, useCallbackকাছাকাছি useRef, বরং একটি উপায় গণনা ফলাফলের memoize চেয়ে।

আমি যে দস্তাবেজগুলিতে সম্মত তা সন্ধান করে এটি বিভ্রান্তিকর দেখাচ্ছে।

useCallbackকলব্যাকের একটি স্মৃতিকৃত সংস্করণ ফিরিয়ে দেবে যা কেবলমাত্র যদি ইনপুটগুলির কোনও একটি পরিবর্তিত হয় তবে তা পরিবর্তন হয়। অপ্রয়োজনীয় রেন্ডারগুলি প্রতিরোধের জন্য রেফারেন্স সাম্যের উপর নির্ভর করে এমন অনুকূল শিশু উপাদানগুলিতে কলব্যাকগুলি পাস করার সময় এটি দরকারী eg

উদাহরণ

মনে করুন আমাদের একটি PureComponentবেসড শিশু রয়েছে <Pure />যা কেবলমাত্র একবার propsপরিবর্তন হয়ে গেলে তা পুনরায় রেন্ডার করবে ।

এই কোডটি প্রতিবার পিতামাতার পুনরায় রেন্ডার হওয়ার সাথে সাথে শিশুটিকে পুনরায় রেন্ডার করে - কারণ ইনলাইন ফাংশনটি প্রতিবারই রেফারেন্টালি আলাদা হয়:

function Parent({ ... }) {
  const [a, setA] = useState(0);
  ... 
  return (
    ...
    <Pure onChange={() => { doSomething(a); }} />
  );
}

এর সাহায্যে আমরা এটি পরিচালনা করতে পারি useCallback:

function Parent({ ... }) {
  const [a, setA] = useState(0);
  const onPureChange = useCallback(() => {doSomething(a);}, []);
  ... 
  return (
    ...
    <Pure onChange={onPureChange} />
  );
}

কিন্তু একবার aআমরা যে পরিবর্তিত হয় onPureChangeএবং আমাদের মনে প্রতিক্রিয়া - - পুরাতন পয়েন্ট এখনও হ্যান্ডলার ফাংশন আমরা নির্মিত aমান! পারফরম্যান্স সমস্যার পরিবর্তে আমরা একটি বাগ পেয়েছি! এটি কারণ ভেরিয়েবল onPureChangeঅ্যাক্সেস করতে একটি ক্লোজার ব্যবহার করে a, যা onPureChangeঘোষিত হওয়ার পরে ধরা পড়েছিল । এটি ঠিক করার জন্য আমাদের কোথায় প্রতিক্রিয়া জানাতে হবে যেখানে onPureChangeসঠিক তথ্যকে নির্দেশ করে এমন একটি নতুন সংস্করণ ড্রপ এবং পুনরায় তৈরি / স্মরণ (স্মৃতিচারণ) করতে হবে। আমরা argument ইউজক্যালব্যাক থেকে দ্বিতীয় যুক্তিতে নির্ভরতাa হিসাবে যুক্ত করে তা করি:

const [a, setA] = useState(0);
const onPureChange = useCallback(() => {doSomething(a);}, [a]);

এখন, যদি aপরিবর্তন করা হয়, প্রতিক্রিয়াটি উপাদানটিকে পুনরায় সরবরাহ করে। এবং পুনঃ-রেন্ডারের সময় এটি দেখতে পায় যে এর জন্য নির্ভরতা onPureChangeআলাদা, এবং কলব্যাকের নতুন সংস্করণটি পুনরায় তৈরি / স্মৃতিচারণ করতে হবে। সবশেষে সব কাজ!

এনবি কেবল PureComponent/ এর জন্য নয় React.memo, কিছুকে নির্ভরতা হিসাবে ব্যবহার করার সময় রেফারেনশিয়াল সাম্যতা সমালোচনামূলক হতে পারে useEffect


19

জন্য এক-লাইনের useCallbackবনাম useMemo:

useCallback(fn, deps)হয় সমতুল্য করার useMemo(() => fn, deps)


আপনার সাথে useCallbackফাংশনগুলিকে useMemoস্মরণ করুন, কোনও গণিত মানকে স্মরণ করিয়ে দিন:

const fn = () => 42 // assuming expensive calculation here
const memoFn = useCallback(fn, [dep]) // (1)
const memoFnReturn = useMemo(fn, [dep]) // (2)

(1)fnএকাধিক রেন্ডার জুড়ে - একই রেফারেন্সের একটি স্মারক সংস্করণ প্রদান করবে যতক্ষণ depনা একই is কিন্তু প্রত্যেক সময় আপনি ডাকা memoFn , যে জটিল গণনার আবার শুরু হয়।

(2)fnপ্রতিবার depপরিবর্তনের জন্য অনুরোধ জানাবে এবং তার ফিরে আসা মানটি ( 42এখানে) মনে রাখবে , যা পরে সঞ্চিত থাকে memoFnReturn


18

আপনি প্রতিটি বার মেমোইজড কলব্যাক কল করছেন, যখন আপনি:

const calcCallback = useCallback(() => expensiveCalc('useCallback'), [neverChange]);
const computedCallback = calcCallback();

এই কারণেই গণনা বাড়ছে useCallback। তবে ফাংশনটি কখনই পরিবর্তিত হয় না, এটি ***** কখনও নতুন কলব্যাক তৈরি করে না, এটি সর্বদা একই the অর্থ useCallbackসঠিকভাবে এটি কাজ করছে।

আসল এটি দেখতে আপনার কোডে কিছু পরিবর্তন করা যাক। আসুন একটি গ্লোবাল ভেরিয়েবল তৈরি করা যাক lastComputedCallback, এটি কোনও নতুন (পৃথক) ফাংশনটি ফিরে আসে কিনা তা ট্র্যাক করে রাখে। যদি কোনও নতুন ফাংশন ফিরে আসে, তার অর্থ useCallbackকেবল "আবার কার্যকর করা হবে"। সুতরাং যখন এটি আবার কার্যকর হবে আমরা কল করব expensiveCalc('useCallback'), আপনি যদি useCallbackকাজ করে থাকেন তবে এইভাবেই আপনি গণনা করছেন । আমি নীচের useCallbackকোডটিতে এটি করি এবং এটি এখন স্পষ্ট যে প্রত্যাশার সাথে মেমোজাইজ হচ্ছে।

তোমাকে দেখতে চাই তাহলে useCallbackঅ্যারের পাস তারপর লাইন uncomment পুনরায় তৈরি ফাংশন সব, second। আপনি এটি ফাংশনটি পুনরায় তৈরি করতে দেখবেন।

'use strict';

const { useState, useCallback, useMemo } = React;

const neverChange = 'I never change';
const oneSecond = 1000;

let lastComputedCallback;
function App() {
  const [second, setSecond] = useState(0);
  
  // This 👇 is not expensive, and it will execute every render, this is fine, creating a function every render is about as cheap as setting a variable to true every render.
  const computedCallback = useCallback(() => expensiveCalc('useCallback'), [
    neverChange,
    // second // uncomment this to make it return a new callback every second
  ]);
  
  
  if (computedCallback !== lastComputedCallback) {
    lastComputedCallback = computedCallback
    // This 👇 executes everytime computedCallback is changed. Running this callback is expensive, that is true.
    computedCallback();
  }
  // This 👇 executes once
  const computedMemo = useMemo(() => expensiveCalc('useMemo'), [neverChange]);
  
  setTimeout(() => setSecond(second + 1), oneSecond);
  return `
    useCallback: ${expensiveCalcExecutedTimes.useCallback} times |
    useMemo: ${computedMemo} |
    App lifetime: ${second}sec.
  `;
}

const tenThousand = 10 * 1000;
let expensiveCalcExecutedTimes = { 'useCallback': 0, 'useMemo': 0 };

function expensiveCalc(hook) {
  let i = 0;
  while (i < 10000) i++;
  
  return ++expensiveCalcExecutedTimes[hook];
}


ReactDOM.render(
  React.createElement(App),
  document.querySelector('#app')
);
<h1>useCallback vs useMemo:</h1>
<div id="app">Loading...</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>

সুবিধার useCallbackতাই প্রতিক্রিয়া হয় না, ফাংশন ফিরে একই যে removeEventListener'ing এবং addEventListenerউপাদান সব উপর ing, যদি না computedCallbackপরিবর্তন। এবং computedCallbackশুধুমাত্র পরিবর্তনশীল পরিবর্তিত হয়। সুতরাং প্রতিক্রিয়া কেবল addEventListenerএকবার হবে ।

দুর্দান্ত প্রশ্ন, আমি এর উত্তর দিয়ে অনেক কিছু শিখেছি।


4
উত্তম উত্তরের জন্য কেবলমাত্র ছোট মন্তব্য: মূল লক্ষ্যটি সম্পর্কে নয় addEventListener/removeEventListener(এটি নিজেরাই ভারী নয় যেহেতু PureComponentshouldComponentUpdate()
ডিওএম রিফ্লো

ধন্যবাদ @ স্কাইবায়ার *EventListenerসস্তা হবার বিষয়ে আমার কোনও ধারণা ছিল না , এটি রিফ্লো / পেইন্টের কারণ না হওয়ার একটি দুর্দান্ত বিষয়! আমি সবসময় মনে করি এটি ব্যয়বহুল তাই আমি এড়াতে চেষ্টা করেছি। সুতরাং যে ক্ষেত্রে আমি কোনটির কাছে যাচ্ছি না PureComponent, useCallbackসেই বাণিজ্যটি কি প্রতিক্রিয়া না করে বাণিজ্যটি বন্ধ করে দেওয়া এবং ডিওএম অতিরিক্ত জটিলতা তৈরি করে remove/addEventListener?
নোটিডার্ট

4
যদি নেস্টেড উপাদানগুলির জন্য ব্যবহার না করে PureComponentবা কাস্টম shouldComponentUpdateব্যবহার useCallbackনা করে তবে কোনও মান যুক্ত করা হবে না (দ্বিতীয় useCallbackবিতর্কিত অতিরিক্ত তদন্তের ফলে ওভারহেড অতিরিক্ত removeEventListener/addEventListenerপদক্ষেপ বাদ দেওয়া বাতিল করবে )
স্কাইবায়ার

বাহ দুর্দান্ত আকর্ষণীয় এটি ভাগ করে নেওয়ার জন্য আপনাকে ধন্যবাদ, এটি আমার কাছে কীভাবে *EventListenerব্যয়বহুল অপারেশন নয় তা সম্পূর্ণ নতুন চেহারা ।
নোটিডার্ট

2

useMemoএবং useCallbackস্মৃতিচারণ ব্যবহার করুন।

আমি স্মরণে কিছু মনে রাখার মতো ভাবতে চাই

উভয়ই useMemoএবং নির্ভরতাগুলি পরিবর্তন না হওয়া অবধি রেন্ডারগুলির মধ্যে কিছু useCallback মনে রাখে , পার্থক্যটি তাদের মনে রাখার মতোই ।

useMemoহবে মনে রাখবেন আপনার ফাংশন থেকে ফেরত মান।

useCallbackহবে মনে রাখবেন আপনার প্রকৃত ফাংশন।

উত্স: ইউজমেমো এবং ইউজক্যালব্যাকের মধ্যে পার্থক্য কী?

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.