সেটইন্টারভাল এর মধ্যে প্রতিক্রিয়া রাষ্ট্রের হুক ব্যবহার করার সময় রাষ্ট্র আপডেট করে না


109

আমি নতুন রিএ্যাক্ট হুক চেষ্টা করছি এবং একটি কাউন্টার সহ একটি ক্লক উপাদান রয়েছে যা প্রতি সেকেন্ডে বাড়ার কথা। তবে মান একেরও বেশি বেড়ে যায় না।

function Clock() {
  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      setTime(time + 1);
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, []);

  return (
    <div>Seconds: {time}</div>
  );
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

উত্তর:


158

কারণটি কারণ কলব্যাকটি setIntervalবন্ধ হয়ে গেছে কেবলমাত্র timeপ্রথম রেন্ডারে ভেরিয়েবলটি অ্যাক্সেস করে , timeপরবর্তী রেন্ডারে এটির নতুন মানটির অ্যাক্সেস থাকে না কারণ useEffect()দ্বিতীয়বার অনুরোধ করা হয়নি।

timesetIntervalকলব্যাকের মধ্যে সর্বদা 0 এর মান থাকে ।

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

বোনাস: বিকল্প পদ্ধতি

ড্যান আব্রামভ, setIntervalতার ব্লগ পোস্টে হুক ব্যবহার করে বিষয়টির গভীরতার সাথে এই সমস্যাটি সম্পর্কে বিকল্প উপায় সরবরাহ করে। উচ্চভাবে এটি পড়ার সুপারিশ!

function Clock() {
  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      setTime(prevTime => prevTime + 1); // <-- Change this line!
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, []);

  return (
    <div>Seconds: {time}</div>
  );
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


4
এটি খুবই দরকারী তথ্য. ধন্যবাদ ইয়াংশুন!
থোলে

8
আপনাকে স্বাগতম, আমি একটি অতি প্রাথমিক (তবে সম্ভবত অ-সুস্পষ্ট?) ত্রুটিটি তৈরি করেছি এবং এটি সম্প্রদায়ের সাথে ভাগ করে নিতে চেয়েছি
কেবলমাত্র কোডটির

4
এর সর্বোত্তম S
প্যাট্রিক হুন্ড

42
হাহাহা আমি এখানে আমার পোস্টটি প্লাগ করতে এসেছি এবং এটি ইতিমধ্যে উত্তরে রয়েছে!
ড্যান আব্রামভ

4
দুর্দান্ত ব্লগ পোস্ট। চোখ খোলা।
বেনজামিনাদক

19

useEffect খালি ইনপুট তালিকা সরবরাহ করা হয় যখন উপাদান মাউন্ট শুধুমাত্র ফাংশন মূল্যায়ন করা হয়।

এর বিকল্পটি setIntervalহ'ল setTimeoutপ্রতিবারের সাথে রাষ্ট্র আপডেট হওয়ার সাথে সাথে নতুন অন্তর স্থাপন করা:

  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = setTimeout(() => {
      setTime(time + 1);
    }, 1000);
    return () => {
      clearTimeout(timer);
    };
  }, [time]);

এর পারফরম্যান্স প্রভাব setTimeoutতাত্পর্যপূর্ণ এবং সাধারণত উপেক্ষা করা যেতে পারে। উপাদানটি যখন বিন্দুতে সময় সংবেদনশীল না হয় যেখানে নতুন সেট টাইমআউটগুলি অনাকাঙ্ক্ষিত প্রভাব সৃষ্টি করে, উভয় setIntervalএবং setTimeoutপদ্ধতির গ্রহণযোগ্য।


12

অন্যরা যেমন উল্লেখ করেছে, সমস্যাটি useStateকেবল একবার বলা হয় ( deps = []অন্তর সেট আপ করার জন্য এটি:

React.useEffect(() => {
    const timer = window.setInterval(() => {
        setTime(time + 1);
    }, 1000);

    return () => window.clearInterval(timer);
}, []);

তারপরে, প্রতিবার setIntervalটিক্স দিলে, এটি আসলে কল করবে setTime(time + 1), তবে কলব্যাক (সমাপনীকরণ) সংজ্ঞায়িত হওয়ার পরে timeশুরুতে এটি সর্বদা মানটি ধরে রাখবে setInterval

আপনি useStateসেটার বিকল্প ফর্মটি ব্যবহার করতে পারেন এবং আপনি যে প্রকৃত মান সেট করতে চান তার চেয়ে কলব্যাক সরবরাহ করতে পারেন (ঠিক যেমন এর সাথে setState):

setTime(prevTime => prevTime + 1);

তবে আমি আপনাকে নিজের useIntervalহুক তৈরি করতে উত্সাহিত করব যাতে আপনি setInterval ঘোষক ব্যবহার করে আপনার কোডটি শুকান এবং সহজ করতে পারেন , যেমন ড্যান আব্রামভ এখানে প্রতিক্রিয়া হুক সহ সেট ইন্টেরালভাল ডিক্লারেটিভ তৈরির পরামর্শ দিয়েছেন :

function useInterval(callback, delay) {
  const intervalRef = React.useRef();
  const callbackRef = React.useRef(callback);

  // Remember the latest callback:
  //
  // Without this, if you change the callback, when setInterval ticks again, it
  // will still call your old callback.
  //
  // If you add `callback` to useEffect's deps, it will work fine but the
  // interval will be reset.

  React.useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  // Set up the interval:

  React.useEffect(() => {
    if (typeof delay === 'number') {
      intervalRef.current = window.setInterval(() => callbackRef.current(), delay);

      // Clear interval if the components is unmounted or the delay changes:
      return () => window.clearInterval(intervalRef.current);
    }
  }, [delay]);
  
  // Returns a ref to the interval ID in case you want to clear it manually:
  return intervalRef;
}


const Clock = () => {
  const [time, setTime] = React.useState(0);
  const [isPaused, setPaused] = React.useState(false);
        
  const intervalRef = useInterval(() => {
    if (time < 10) {
      setTime(time + 1);
    } else {
      window.clearInterval(intervalRef.current);
    }
  }, isPaused ? null : 1000);

  return (<React.Fragment>
    <button onClick={ () => setPaused(prevIsPaused => !prevIsPaused) } disabled={ time === 10 }>
        { isPaused ? 'RESUME ⏳' : 'PAUSE 🚧' }
    </button>

    <p>{ time.toString().padStart(2, '0') }/10 sec.</p>
    <p>setInterval { time === 10 ? 'stopped.' : 'running...' }</p>
  </React.Fragment>);
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
body,
button {
  font-family: monospace;
}

body, p {
  margin: 0;
}

p + p {
  margin-top: 8px;
}

#app {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-height: 100vh;
}

button {
  margin: 32px 0;
  padding: 8px;
  border: 2px solid black;
  background: transparent;
  cursor: pointer;
  border-radius: 2px;
}
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

সহজ এবং ক্লিনার কোড উত্পাদন করা ছাড়াও, এটি আপনাকে স্বয়ংক্রিয়ভাবে পাস করার মাধ্যমে বিরতি দেওয়ার (এবং সাফ করা) অনুমতি দেয় এবং বিরতি আইডিও delay = nullফেরত দেয়, যদি আপনি নিজে নিজে এটি বাতিল করতে চান (এটি ড্যানের পোস্টগুলিতে আচ্ছাদিত নয়)।

প্রকৃতপক্ষে, এটিকে আরও উন্নত করা যেতে পারে যাতে delayবিরতিযুক্ত হলে এটি পুনরায় আরম্ভ না করে তবে আমি মনে করি বেশিরভাগ ব্যবহারের ক্ষেত্রে এটি যথেষ্ট ভাল good

আপনি যদি এর setTimeoutচেয়ে বেশি অনুরূপ উত্তর খুঁজছেন তবে এটি দেখুন setInterval: https://stackoverflow.com/a/59274757/3723993

এছাড়াও আপনি এর ঘোষণামূলক সংস্করণ খুঁজে পেতে পারেন setTimeoutএবং setInterval, useTimeoutএবং useInterval, প্লাস একটি কাস্টম useThrottledCallbackহুক মধ্যে টাইপ করা বিষয় লেখা https://gist.github.com/Danziger/336e75b6675223ad805a88c2dfdcfd4a


5

একটি বিকল্প সমাধান হ'ল ব্যবহার করা হবে useReducer, কারণ এটি সর্বদা বর্তমান অবস্থায় চলে যাবে।

function Clock() {
  const [time, dispatch] = React.useReducer((state = 0, action) => {
    if (action.type === 'add') return state + 1
    return state
  });
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      dispatch({ type: 'add' });
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, []);

  return (
    <div>Seconds: {time}</div>
  );
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


useEffectসময় আপডেট করার জন্য এখানে কেন একাধিকবার বলা হচ্ছে, যখন নির্ভরতা অ্যারেটি খালি, যার অর্থ useEffectকেবলমাত্র প্রথম বার উপাদান বা অ্যাপ্লিকেশন রেন্ডার বলা উচিত?
ব্ল্যাকম্যাথ

4
@ ব্ল্যাকম্যাথ ভিতরে ফাংশনটি useEffectকেবল একবার বলা হয়, যখন উপাদানটি প্রথম সত্যই রেন্ডার করে। তবে এর অভ্যন্তরে এমন একটি রয়েছে setIntervalযা নিয়মিতভাবে সময় পরিবর্তনের দায়িত্বে থাকে। আমি আপনাকে কিছুটা পড়ার পরামর্শ দিই setInterval, তার পরে বিষয়গুলি আরও পরিষ্কার হওয়া উচিত! বিকাশকারী.মোজিলা.আর.ইন-
ইউএস / ডকস

3

নীচে হিসাবে এটি কাজ করে।

const [count , setCount] = useState(0);

async function increment(count,value) {
    await setCount(count => count + 1);
  }

//call increment function
increment(count);

count => count + 1কলব্যাক কি, আমার জন্য কাজ ধন্যবাদ ছিল!
নিকফোথিম

1

এই সমাধানগুলি আমার পক্ষে কাজ করে না কারণ আমাকে ভেরিয়েবলটি পাওয়া দরকার এবং কেবল আপডেট না করে কিছু স্টাফ করা দরকার।

আমি একটি প্রতিশ্রুতি দিয়ে হুকের আপডেট হওয়া মান পাওয়ার জন্য একটি কার্যপ্রণালী পেতে পারি

যেমন:

async function getCurrentHookValue(setHookFunction) {
  return new Promise((resolve) => {
    setHookFunction(prev => {
      resolve(prev)
      return prev;
    })
  })
}

এটির সাহায্যে আমি সেট ইনটারভাল ফাংশনটির মতো মানটি পেতে পারি

let dateFrom = await getCurrentHackValue(setSelectedDateFrom);

0

সময় পরিবর্তিত হলে রিঅ্যাক্টকে রি-রেন্ডার বলুন। অপ্ট আউট

function Clock() {
  const [time, setTime] = React.useState(0);
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      setTime(time + 1);
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, [time]);

  return (
    <div>Seconds: {time}</div>
  );
}

ReactDOM.render(<Clock />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


4
এটির সাথে সমস্যাটি হ'ল টাইমার সাফ হয়ে যাবে এবং প্রতিটি countপরিবর্তনের পরে পুনরায় সেট হবে।
21:51 '

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