প্রতিক্রিয়া হুক ব্যবহারের প্রভাবতে ওল্ডভ্যালুগুলি এবং নতুন ভ্যালুগুলি কীভাবে তুলনা করবেন?


149

ধরা যাক আমার কাছে 3 টি ইনপুট রয়েছে: রেট, সেন্ডআমউন্ট এবং রিসিভআমাউন্ট। আমি 3 টি ইনপুটগুলি ব্যবহারের বিভিন্ন পারফরমেন্সগুলিতে রাখি। বিধিগুলি হ'ল:

  • যদি সেন্ডআমাউন্ট পরিবর্তিত হয়, আমি গণনা করি receiveAmount = sendAmount * rate
  • যদি প্রাপ্তিমাউন্ট পরিবর্তন হয় তবে আমি গণনা করি sendAmount = receiveAmount / rate
  • যদি হার পরিবর্তন হয় তবে আমি receiveAmount = sendAmount * rateকখন sendAmount > 0এবং sendAmount = receiveAmount / rateকখন গণনা করি তা গণনা করিreceiveAmount > 0

সমস্যাটি প্রদর্শনের জন্য কোডসবক্স https://codesandbox.io/s/pkl6vn7x6j এখানে ।

সেখানে তুলনা করার জন্য একটি উপায় আছে কি oldValuesএবং newValuesপছন্দ componentDidUpdateপরিবর্তে এই ক্ষেত্রে 3 হ্যান্ডেলার তৈরীর?

ধন্যবাদ


Https://codesandbox.io/s/30n01w2r06 এর সাথে আমার চূড়ান্ত সমাধান এখানেusePrevious

এই ক্ষেত্রে, আমি একাধিকটি ব্যবহার করতে পারি না useEffectকারণ প্রতিটি পরিবর্তন একই নেটওয়ার্ক কলের দিকে পরিচালিত করে। এজন্য আমিও changeCountপরিবর্তনটি ট্র্যাক করতে ব্যবহার করি। এটি changeCountকেবল স্থানীয় থেকে পরিবর্তনগুলি ট্র্যাক করতেও সহায়ক, তাই সার্ভারের পরিবর্তনের কারণে আমি অপ্রয়োজনীয় নেটওয়ার্ক কলকে আটকাতে পারি।


কীভাবে কম্পোনেন্টডিডআপেটকে সাহায্য করার কথা রয়েছে? আপনার এখনও এই 3 শর্ত লিখতে হবে।
এস্তাস ফ্লাস্ক

আমি 2 টি alচ্ছিক সমাধান সহ একটি উত্তর যুক্ত করেছি। তাদের মধ্যে কি আপনার পক্ষে কাজ করে?
বেন কার্প

উত্তর:


207

আপনি ইউজারিফ ব্যবহার করে আপনাকে পূর্ববর্তী প্রপস সরবরাহ করতে একটি কাস্টম হুক লিখতে পারেন

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

এবং তারপরে এটি ব্যবহার করুন useEffect

const Component = (props) => {
    const {receiveAmount, sendAmount } = props
    const prevAmount = usePrevious({receiveAmount, sendAmount});
    useEffect(() => {
        if(prevAmount.receiveAmount !== receiveAmount) {

         // process here
        }
        if(prevAmount.sendAmount !== sendAmount) {

         // process here
        }
    }, [receiveAmount, sendAmount])
}

তবে এটি পরিষ্কার এবং সম্ভবত আরও ভাল এবং আরও পরিষ্কার এবং পড়া এবং বুঝতে আপনি useEffectপ্রতিটি পরিবর্তনের আইডির জন্য দুটি পৃথকভাবে ব্যবহার করেন আপনি সেগুলি পৃথকভাবে প্রক্রিয়া করতে চান


8
useEffectপৃথকভাবে দুটি কল ব্যবহার করার জন্য নোটটির জন্য ধন্যবাদ । আপনি একাধিকবার এটি ব্যবহার করতে পারবেন তা অবগত ছিল না!
জেসনএইচ

3
আমি উপরের কোডটি চেষ্টা করেছি, তবে এসলিন্ট আমাকে সতর্ক করে দিচ্ছে যে useEffectনির্ভরতা অনুপস্থিতprevAmount
লিটল

2
যেহেতু প্রাক-সামঞ্জস্যের রাষ্ট্র / প্রপসগুলির পূর্বের মানটির মূল্য রয়েছে, তাই আপনাকে এটি ব্যবহারের প্রভাব হিসাবে নির্ভর করতে হবে না এবং আপনি নির্দিষ্ট ক্ষেত্রে এই সতর্কতাটি অক্ষম করতে পারেন। আপনি আরও বিশদ জন্য এই পোস্ট পড়তে পারেন?
শুভম খাতরী

এটি পছন্দ করুন - ইউজারিফ সর্বদা উদ্ধার করতে আসে।
ফার্নান্দো রোজো

@ শুভমখাত্রী: রেফকন্টারেন্ট = মানটি ব্যবহারের কার্যকর মোড়কের কারণ কী?
curly_bracket

40

যেহেতু কেউ ব্যবহারের একটি টাইপস্ক্রিপ্ট সংস্করণ সন্ধান করছে পূর্বে:

import { useEffect, useRef } from "react";

const usePrevious = <T extends {}>(value: T): T | undefined => {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

2
নোট করুন যে এটি কোনও টিএসএক্স ফাইলে কাজ করবে না, const usePrevious = <T extends any>(...দোভাষীকে এটি তৈরি করতে টিএসএক্স ফাইলের পরিবর্তনের জন্য কাজটি করতে এটি মনে করুন যে <T> জেএসএক্স নয় এবং সাধারণ
জোর-

1
কাজ করতে <T extends {}>হবে না কেন আপনি ব্যাখ্যা করতে পারেন । এটি আমার পক্ষে কাজ করছে বলে মনে হচ্ছে তবে আমি এটির ব্যবহারের জটিলতা বোঝার চেষ্টা করছি।
কার্তিকেয়ান_কেকে

.tsxফাইলগুলিতে জেএসএক্স থাকে যা বোঝানো হয় এইচটিএমএল-তে অভিন্ন। এটা সম্ভব যে <T>জেনেরিকটি একটি অনিবদ্ধ উপাদান ট্যাগ।
SeedyROM

রিটার্ন টাইপ সম্পর্কে কি? আমি পেয়েছিMissing return type on function.eslint(@typescript-eslint/explicit-function-return-type)
সাবা আহং

@ সাবাআহ্যাং দুঃখিত! টিএস বেশিরভাগ অংশের রিটার্নের ধরণগুলি হ্যান্ডেল করতে টাইপ ইনফারেন্স ব্যবহার করবে তবে আপনি যদি লিটিংটি সক্ষম করে থাকেন তবে আমি সতর্কতা থেকে মুক্তি পেতে একটি রিটার্ন টাইপ যুক্ত করেছি।
SeedyROM

25

বিকল্প 1 - হুক ব্যবহার করুন

আগের মানটির সাথে বর্তমান মানটির তুলনা করা একটি সাধারণ প্যাটার্ন এবং এটি নিজস্ব নিজস্ব কাস্টম হুককে ন্যায্যতা দেয় যা প্রয়োগের বিশদটি গোপন করে।

const useCompare = (val: any) => {
    const prevVal = usePrevious(val)
    return prevVal !== val
}

const usePrevious = (value) => {
    const ref = useRef();
    useEffect(() => {
      ref.current = value;
    });
    return ref.current;
}

const Component = (props) => {
  ...
  const hasVal1Changed = useCompare(val1)
  const hasVal2Changed = useCompare(val2);
  useEffect(() => {
    if (hasVal1Changed ) {
      console.log("val1 has changed");
    }
    if (hasVal2Changed ) {
      console.log("val2 has changed");
    }
  });

  return <div>...</div>;
};

ডেমো

বিকল্প 2 - রান পরিবর্তন করুন যখন মান পরিবর্তন হয়

const Component = (props) => {
  ...
  useEffect(() => {
    console.log("val1 has changed");
  }, [val1]);
  useEffect(() => {
    console.log("val2 has changed");
  }, [val2]);

  return <div>...</div>;
};

ডেমো


দ্বিতীয় বিকল্পটি আমার পক্ষে কাজ করেছিল। আপনি দয়া করে আমাকে গাইড করতে পারেন কেন আমাকে ব্যবহারের দু'বার ব্যবহার করতে হবে?
তরুণ নাগপাল

@ তরুন নাগপাল, আপনার প্রয়োজনবোধে দুবার ইউজএফেক্ট ব্যবহার করার দরকার নেই। এটি আপনার ব্যবহারের ক্ষেত্রে নির্ভর করে। কল্পনা করুন যে আমরা কেবল লগ করতে চাই যা কোন ভাল বদলেছে। আমাদের নির্ভরতার অ্যারেতে যদি আমাদের মধ্যে ভ্যাল 1 এবং ভাল 2 থাকে তবে এটি প্রত্যেকবার মানগুলির কোনও পরিবর্তন হয়ে গেলে চলবে। তারপরে আমরা যে ফাংশনটি পাস করি তার ভিতরে আমাদের খুঁজে বের করতে হবে যে সঠিক বার্তায় লগ করতে কোন ভ্যাল বদলেছে।
বেন কার্প

উত্তরের জন্য ধন্যবাদ. আপনি যখন বলেন "ফাংশনটির ভিতরে আমরা পাস করি তখন আমাদের কোন ভ্যালির পরিবর্তন হয়েছে তা খুঁজে বের করতে হবে"। আমরা কীভাবে এটি অর্জন করতে পারি। দয়া করে গাইড করুন
তরুণ নাগপাল

6

আমি স্রেফ রিএ্যাক্ট-ডেল্টা প্রকাশ করেছি যা এই সঠিক দৃশ্যের সমাধান করে। আমার মতে, useEffectঅনেক দায়িত্ব আছে।

দায়িত্ব

  1. এটি তার নির্ভরতা অ্যারে ব্যবহার করে সমস্ত মানকে তুলনা করে Object.is
  2. এটি # 1 এর ফলাফলের ভিত্তিতে প্রভাব / ক্লিনআপ কলব্যাক চালায়

দায়িত্ব ভেঙে দেওয়া

react-deltauseEffectবেশ কয়েকটি ছোট ছোট কুকুরগুলির দায়িত্ব ভেঙে দেয় ।

দায়িত্ব # 1

দায়িত্ব # 2

আমার অভিজ্ঞতায়, এই পদ্ধতির useEffect/ useRefসমাধানগুলির চেয়ে আরও নমনীয়, পরিষ্কার এবং সংক্ষিপ্ত ।


আমি যা খুঁজছি এটি চেষ্টা করে দেখুন!
হ্যাকারএল 33t

দেখতে সত্যিই ভাল লাগছে তবে কিছুটা ওভারকিল নয়?
হিসাটো

@ হিসাতো এটি খুব ভালভাবে ওভারকিল হতে পারে। এটি একটি পরীক্ষামূলক API এর কিছু something এবং প্রকৃতপক্ষে আমি আমার দলগুলির মধ্যে এটি বেশি ব্যবহার করি নি কারণ এটি ব্যাপকভাবে পরিচিত বা গৃহীত হয়নি। তত্ত্বগতভাবে এটি দুর্দান্ত ধরণের বলে মনে হয়, তবে বাস্তবে এটি উপযুক্ত নয়।
অস্টিন মালেরারবা

3

যেহেতু রাজ্য কার্যকরী উপাদানগুলিতে উপাদান উদাহরণের সাথে শক্তভাবে মিলিত হয় না, তাই পূর্ববর্তী অবস্থায় useEffectপ্রথমে এটি সংরক্ষণ না করে প্রবেশ করা যায় না , উদাহরণস্বরূপ, এর সাথে useRef। এর অর্থ হ'ল স্টেট আপডেট সম্ভবত ভুল জায়গায় কার্যকরভাবে প্রয়োগ করা হয়েছিল কারণ আপডেটের setStateকার্যকারণের অভ্যন্তরে পূর্ববর্তী রাজ্য উপলব্ধ ।

এটি একটি ভাল ব্যবহারের ক্ষেত্রে useReducerযার জন্য রেডাক্সের মতো স্টোর সরবরাহ করে এবং সংশ্লিষ্ট প্যাটার্নটি প্রয়োগ করতে দেয়। রাষ্ট্রীয় আপডেটগুলি সুস্পষ্টভাবে সঞ্চালিত হয়, সুতরাং কোন রাষ্ট্রীয় সম্পত্তি আপডেট হয়েছে তা নির্ধারণের প্রয়োজন নেই; এটি প্রেরিত ক্রিয়া থেকে ইতিমধ্যে পরিষ্কার।

এটি দেখতে কেমন হতে পারে তার একটি উদাহরণ এখানে দেওয়া হয়েছে :

function reducer({ sendAmount, receiveAmount, rate }, action) {
  switch (action.type) {
    case "sendAmount":
      sendAmount = action.payload;
      return {
        sendAmount,
        receiveAmount: sendAmount * rate,
        rate
      };
    case "receiveAmount":
      receiveAmount = action.payload;
      return {
        sendAmount: receiveAmount / rate,
        receiveAmount,
        rate
      };
    case "rate":
      rate = action.payload;
      return {
        sendAmount: receiveAmount ? receiveAmount / rate : sendAmount,
        receiveAmount: sendAmount ? sendAmount * rate : receiveAmount,
        rate
      };
    default:
      throw new Error();
  }
}

function handleChange(e) {
  const { name, value } = e.target;
  dispatch({
    type: name,
    payload: value
  });
}

...
const [state, dispatch] = useReducer(reducer, {
  rate: 2,
  sendAmount: 0,
  receiveAmount: 0
});
...

হাই @estus, এই ধারণার জন্য ধন্যবাদ। এটি আমাকে ভাবনার আরেকটি উপায় দেয়। তবে, আমি উল্লেখ করতে ভুলে গেছি যে প্রতিটি মামলার জন্য আমাকে এপিআই কল করা দরকার। আপনার কি এর কোনও সমাধান আছে?
রুইনঝাং

আপনি কি বোঝাতে চেয়েছেন? হ্যান্ডেল চেঞ্জের ভিতরে? 'এপিআই' এর অর্থ কি দূরবর্তী এপিআই সমাপ্তি? আপনি কি উত্তর সহ কোডসডবক্স আপডেট করতে পারবেন?
এস্তাস ফ্লাস্ক

আপডেটটি থেকে আমি ধরে নিতে পারি যে আপনি আনয়ন করতে চান sendAmount, ইত্যাদি ইত্যাদি হিসাব করার পরিবর্তে অবিচ্ছিন্নভাবে, ডান? এটি অনেক পরিবর্তন হয়। এটি দিয়ে করা সম্ভব useReduceতবে জটিল হতে পারে। আপনি যদি রেডাক্সের সাথে ডিল করেন তবে আপনি জানেন যে অ্যাসিঙ্ক অপারেশনগুলি সোজাভাবে এখানে নেই। github.com/reduxjs/redux-thunk Redux এর জন্য একটি জনপ্রিয় এক্সটেনশন যা অ্যাসিঙ্ক ক্রিয়াকলাপের অনুমতি দেয়। এখানে এমন একটি ডেমো যা একই প্যাটার্ন (ইউজডটঙ্করডুসার), কোডস্যান্ডবক্স.আইও / এস / z জেজেআরআরআইআরআইআর সাথে ব্যবহারকারীর রিডুসার যুক্ত করে । লক্ষ্য করুন যে প্রেরণ ফাংশনটি হল async, আপনি সেখানে অনুরোধ করতে পারেন (মন্তব্য করেছেন)।
এস্তাস ফ্লাস্ক

1
প্রশ্নের সমস্যাটি হ'ল আপনি এমন একটি আলাদা জিনিস সম্পর্কে জিজ্ঞাসা করেছিলেন যা আপনার আসল ঘটনা নয় এবং এরপরে এটি পরিবর্তন হয়েছিল, সুতরাং এখন এটি একেবারেই আলাদা প্রশ্ন রয়েছে যখন বিদ্যমান উত্তরগুলি প্রদর্শিত হচ্ছে যেন তারা কেবল প্রশ্নটিকে উপেক্ষা করেছে। এটি এসও-তে প্রস্তাবিত অনুশীলন নয় কারণ এটি আপনাকে চাইলে উত্তর পেতে আপনাকে সহায়তা করে না। দয়া করে, উত্তরগুলি ইতিমধ্যে উল্লেখ করা প্রশ্ন থেকে গুরুত্বপূর্ণ অংশগুলি সরিয়ে ফেলবেন না (গণনার সূত্র)। আপনি যদি এখনও আমার আগের মন্তব্যের পরে সমস্যা (যদি (আপনি সম্ভবত না), বিবেচনা একটি নতুন প্রশ্ন যে আপনার আগের প্রয়াস যেমন এই এক আপনার ক্ষেত্রে এবং লিঙ্ক প্রতিফলিত জিজ্ঞাসা করছিলেন।
Estus বোতল

হাই এস্টাস, আমি মনে করি এই কোডসন্ডবক্স কোডস্যান্ডবক্স.আইও / এস / z জেডআরআরআইআরআর এখনও আপডেট হয়নি। আমি প্রশ্নটি পরিবর্তন করব, এর জন্য দুঃখিত।
রুইনঝাং

3

রেফ ব্যবহার করে অ্যাপে নতুন ধরণের বাগ প্রবর্তিত হবে।

এর usePreviousআগে কেস মন্তব্য করেছে এমনটি ব্যবহার করে এই কেসটি দেখুন:

  1. প্রোপমিনটাইম: 5 ==> রেফকন্টেন্ট = 5 | সেট করুন
  2. প্রোপমিনটাইম: 5 ==> রেফকন্টেন্ট = 5 | নতুন মান রেফকন্টেন্টের সমান
  3. প্রোপমিনটাইম: 8 ==> রেফকন্টেন্ট = 5 | নতুন মান রেফকন্টেন্টের সমান নয়
  4. প্রোপমিনটাইম: 5 ==> রেফকন্টেন্ট = 5 | নতুন মান রেফকন্টেন্টের সমান

আমরা এখানে দেখতে পাচ্ছি, আমরা অভ্যন্তরীণ আপডেট করছি না refকারণ আমরা ব্যবহার করছিuseEffect


1
প্রতিক্রিয়াটির এই উদাহরণ রয়েছে তবে তারা ব্যবহার করছে state... এবং না props... আপনি যখন পুরানো প্রপসগুলি যত্ন করবেন তখন ত্রুটি ঘটবে।
স্যান্টোমেগনজালো

3

সত্যিই সহজ প্রোপ তুলনা করার জন্য আপনি কোনও প্রপস আপডেট হয়েছে কিনা তা সহজেই পরীক্ষা করে দেখতে ইউজএফেক্ট ব্যবহার করতে পারেন।

const myComponent = ({ prop }) => {
  useEffect(() => {
     ---Do stuffhere----
    }
  }, [prop])

}

ইউজএফেক্টটি তখনই প্রপ পরিবর্তন হলে আপনার কোডটি চালাবে।


1
এটি কেবল একবারে কাজ করে, একটানা propপরিবর্তনের পরেও আপনি সক্ষম হবেন না~ do stuff here ~
করোলিস Šarapnickis

2
দেখে মনে হচ্ছে আপনার রাজ্যের "রিসেট" করার setHasPropChanged(false)জন্য শেষের দিকে ফোন করা উচিত ~ do stuff here ~। (তবে এটি অতিরিক্ত রেন্ডারে পুনরায় সেট হবে)
কেভিন ওয়াং

প্রতিক্রিয়ার জন্য ধন্যবাদ, আপনি উভয়ই ঠিক, আপডেট সমাধান
চার্লি টুপম্যান

@ অ্যান্টোনিওপাভিস্কেভাক-অর্টিজ আমি এখনই প্রোপহ্যাস চ্যাঞ্জড উত্তরটি সত্য হিসাবে রেন্ডার করার জন্য উত্তরটি আপডেট করেছি যা এটি একবার রেন্ডার করার পরে কল করবে, কেবল ব্যবহারের প্রভাব ছড়িয়ে দিতে এবং প্রপটি পরীক্ষা করতে আরও ভাল সমাধান হতে পারে
চার্লি

আমি মনে করি এটির আমার আসল ব্যবহারটি হারিয়ে গেছে। কোডটি ফিরে দেখে আপনি কেবল ব্যবহারের ব্যবহারটি ব্যবহার করতে পারেন
চার্লি

2

গৃহীত উত্তরটি বন্ধ করা, একটি বিকল্প সমাধান যার জন্য কাস্টম হুকের প্রয়োজন নেই:

const Component = (props) => {
  const { receiveAmount, sendAmount } = props;
  const prevAmount = useRef({ receiveAmount, sendAmount }).current;
  useEffect(() => {
    if (prevAmount.receiveAmount !== receiveAmount) {
     // process here
    }
    if (prevAmount.sendAmount !== sendAmount) {
     // process here
    }
    return () => { 
      prevAmount.receiveAmount = receiveAmount;
      prevAmount.sendAmount = sendAmount;
    };
  }, [receiveAmount, sendAmount]);
};

1
ভাল সমাধান, তবে সিনট্যাক্স ত্রুটি রয়েছে। useRefনা userRef। বর্তমান ব্যবহার করতে ভুলে prevAmount.current
গিয়েছেন

0

এখানে আমি একটি কাস্টম হুক ব্যবহার করি যা আমি বিশ্বাস করি এটি ব্যবহারের চেয়ে বেশি স্বজ্ঞাত usePrevious

import { useRef, useEffect } from 'react'

// useTransition :: Array a => (a -> Void, a) -> Void
//                              |_______|  |
//                                  |      |
//                              callback  deps
//
// The useTransition hook is similar to the useEffect hook. It requires
// a callback function and an array of dependencies. Unlike the useEffect
// hook, the callback function is only called when the dependencies change.
// Hence, it's not called when the component mounts because there is no change
// in the dependencies. The callback function is supplied the previous array of
// dependencies which it can use to perform transition-based effects.
const useTransition = (callback, deps) => {
  const func = useRef(null)

  useEffect(() => {
    func.current = callback
  }, [callback])

  const args = useRef(null)

  useEffect(() => {
    if (args.current !== null) func.current(...args.current)
    args.current = deps
  }, deps)
}

আপনি useTransitionনিম্নলিখিত হিসাবে ব্যবহার করব ।

useTransition((prevRate, prevSendAmount, prevReceiveAmount) => {
  if (sendAmount !== prevSendAmount || rate !== prevRate && sendAmount > 0) {
    const newReceiveAmount = sendAmount * rate
    // do something
  } else {
    const newSendAmount = receiveAmount / rate
    // do something
  }
}, [rate, sendAmount, receiveAmount])

আশা করি এইটি কাজ করবে.

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