प्रतिक्रिया हुक पर `setState` कॉलबैक का उपयोग कैसे करें


133

useStateघटक राज्य की स्थापना के लिए रिएक्ट हुक परिचय । लेकिन मैं नीचे दिए गए कोड की तरह कॉलबैक को बदलने के लिए हुक का उपयोग कैसे कर सकता हूं:

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

मैं राज्य के अपडेट होने के बाद कुछ करना चाहता हूं।

मुझे पता है कि मैं useEffectअतिरिक्त चीजें करने के लिए उपयोग कर सकता हूं लेकिन मुझे राज्य के पिछले मूल्य की जांच करनी होगी जिसके लिए थोड़ा कोड की आवश्यकता होती है। मैं एक सरल समाधान की तलाश में हूं जिसका उपयोग useStateहुक के साथ किया जा सकता है ।


1
कक्षा के घटक में, मैंने async का उपयोग किया और उसी परिणाम को प्राप्त करने का इंतजार किया जैसे आपने setState में कॉलबैक जोड़ने के लिए किया था। दुर्भाग्य से, यह हुक में काम नहीं कर रहा है। यहां तक ​​कि अगर मैंने async और इंतजार किया, तो भी प्रतिक्रिया अपडेट के लिए राज्य की प्रतीक्षा नहीं करेगी। हो सकता है कि UseEffect इसे करने का एकमात्र तरीका है।
मिंग वू

2
@ झाओ, आपने अभी तक सही उत्तर नहीं दिया है। क्या आप कृपया कुछ सेकंड के लिए छोड़ सकते हैं
ज़ोहैब इज़ाज़

जवाबों:


148

useEffectइसे प्राप्त करने के लिए आपको हुक का उपयोग करने की आवश्यकता है ।

const [counter, setCounter] = useState(0);

const doSomething = () => {
  setCounter(123);
}

useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);

55
यह console.logपहले रेंडर के साथ-साथ किसी भी समय counterपरिवर्तन पर आग लगाएगा। क्या होगा यदि आप केवल राज्य के अद्यतन होने के बाद ही कुछ करना चाहते हैं लेकिन प्रारंभिक रेंडर पर नहीं क्योंकि प्रारंभिक मूल्य निर्धारित है? मुझे लगता है कि आप मूल्य की जांच कर सकते हैं useEffectऔर तय कर सकते हैं कि आप कुछ करना चाहते हैं। क्या इसे सबसे अच्छा अभ्यास माना जाएगा?
डैरिल यंग

2
useEffectशुरुआती रेंडर पर चलने से बचने के लिए, आप एक कस्टम useEffectहुक बना सकते हैं , जो शुरुआती रेंडर पर नहीं चलता है। इस तरह के हुक बनाने के लिए, आप इस प्रश्न को देख सकते हैं: stackoverflow.com/questions/53253940/…

14
और उस मामले के बारे में क्या है, जब मैं अलग-अलग सेटस्टेट कॉल में अलग-अलग कॉलबैक कॉल करना चाहता हूं, जो एक ही राज्य मान को बदल देगा? आप उत्तर गलत है, और newbies को भ्रमित नहीं करने के लिए सही के रूप में चिह्नित नहीं किया जाना चाहिए। यह सच है कि सेटस्टैट कॉलबैक एक वर्ग के हुक से पलायन करते समय सबसे कठिन समस्याओं में से एक है, जो एक स्पष्ट समाधान विधि नहीं है। कभी-कभी आप वास्तव में कुछ मूल्य-आधारित प्रभाव के साथ व्यवहार करते हैं, और कभी-कभी इसे कुछ हैक करने के तरीकों की आवश्यकता होती है, जैसे कि Ref में कुछ प्रकार के झंडे को सहेजना।
दिमित्री लोबोव

जाहिरा तौर पर, आज, मैं सेटकाउंटर (मूल्य, कॉलबैक) कह सकता हूं, ताकि राज्य के अपडेट होने के बाद कुछ कार्य निष्पादित हो, एक उपयोग से बचना। मुझे पूरी तरह से यकीन नहीं है कि यह एक वरीयता या एक विशेष लाभ है।
केन इंग्राम

2
@KenIngram मैंने बस यही कोशिश की और इसे बहुत ही विशिष्ट त्रुटि मिली:Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().
ग्रेग सैडेट्स्की

25

यदि आप पिछली स्थिति को अपडेट करना चाहते हैं तो आप हुक में इस तरह कर सकते हैं:

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


setCount(previousCount => previousCount + 1);

2
मुझे लगता है कि setCounterआपके setCount
कहने का

@BimalGrg क्या यह वास्तव में काम करता है? मैं इसे दोहरा नहीं सकता। यह इस त्रुटि के साथ संकलित करने में विफल रहता है:expected as assignment or function call and instead saw an expression
टोनिटोन120

@ tonitone120 सेटकाउंट के अंदर एक एरो फंक्शन है।
बिमल ग्राम

@BimalGrg यह प्रश्न राज्य के अद्यतन होने के तुरंत बाद कोड निष्पादित करने की क्षमता के लिए पूछ रहा है। क्या यह कोड वास्तव में उस कार्य में मदद करता है?
tonitone120

हाँ, यह काम करेंगे, के रूप में this.setState में की तरह वर्ग घटकों में,
आशिक अहमद

19

useEffect, कि केवल राज्य अद्यतन पर आग :

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)

useEffect(() => {
  if (!isFirstRender.current) {
    console.log(state) // do something after state has updated
  }
}, [state])

useEffect(() => { 
  isFirstRender.current = false // toggle flag after first render/mounting
}, [])

ऊपर setStateकॉलबैक की सबसे अच्छी नकल करेगा और प्रारंभिक स्थिति के लिए आग नहीं । आप इसमें से एक कस्टम हुक निकाल सकते हैं:

function useEffectUpdate(effect, deps) {
  const isFirstRender = useRef(true) 

  useEffect(() => {
    if (!isFirstRender.current) {
      effect()
    }  
  }, deps) // eslint-disable-line react-hooks/exhaustive-deps

  useEffect(() => {
    isFirstRender.current = false;
  }, []);
}

// ... Usage inside component
useEffectUpdate(() => { console.log(state) }, [state])

वैकल्पिक: कक्षाओं में कॉलबैक प्राप्त useState करने के लिए लागू किया जा सकता हैsetState
ford04

17

मुझे लगता है, का उपयोग useEffectकरना एक सहज तरीका नहीं है।

मैंने इसके लिए एक रैपर बनाया। इस कस्टम हुक में, आप setStateपैरामीटर के बजाय अपने कॉलबैक को पैरामीटर में प्रसारित कर सकते हैं useState

मैंने सिर्फ टाइपस्क्रिप्ट संस्करण बनाया है। इसलिए यदि आपको जावास्क्रिप्ट में इसका उपयोग करने की आवश्यकता है, तो कोड से कुछ प्रकार का अंकन हटा दें।

प्रयोग

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

घोषणा

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback;

कमी

यदि पारित तीर फ़ंक्शन एक चर बाहरी फ़ंक्शन का संदर्भ देता है, तो यह राज्य के अद्यतन होने के बाद वर्तमान मूल्य पर कब्जा नहीं करेगा। उपरोक्त उपयोग के उदाहरण में, कंसोल.लॉग (राज्य) 1 नहीं 2 को प्रिंट करेगा।


1
धन्यवाद! शांत दृष्टिकोण।
ValYouW

@ValYouW नमूना बनाने के लिए धन्यवाद। एकमात्र मामला यह है कि यदि पारित तीर फ़ंक्शन चर बाहरी फ़ंक्शन का संदर्भ देता है, तो यह वर्तमान मानों को कैप्चर करेगा जो कि स्टेट अपडेट होने के बाद नहीं है।
एमजे स्टूडियो

हां, यह कार्यात्मक घटकों के साथ आकर्षक भाग है ...
ValYouW

पिछले राज्य का मूल्य यहाँ कैसे प्राप्त करें?
अंकन-ज़ेरोब

@ अंकन-ज़ेरोब मुझे लगता है, उपयोग भाग में, stateकॉलबैक कहे जाने पर पिछले एक का जिक्र है। है ना?
MJ स्टूडियो 12

2

setState() घटक अवस्था में परिवर्तन करता है और प्रतिक्रिया को बताता है कि इस घटक और उसके बच्चों को अद्यतन स्थिति के साथ फिर से प्रस्तुत करने की आवश्यकता है।

setState पद्धति अतुल्यकालिक है, और तथ्य की बात के रूप में, यह एक वादा वापस नहीं करता है। इसलिए ऐसे मामलों में जहां हम किसी फ़ंक्शन को अपडेट या कॉल करना चाहते हैं, फ़ंक्शन को सेटबैक फ़ंक्शन में कॉलबैक कहा जा सकता है दूसरा तर्क। उदाहरण के लिए, ऊपर दिए गए आपके मामले में, आपने एक फ़ंक्शन को एक सेटस्टैट कॉलबैक कहा है।

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

उपरोक्त कोड वर्ग घटक के लिए ठीक काम करता है, लेकिन कार्यात्मक घटक के मामले में, हम सेटस्टेट विधि का उपयोग नहीं कर सकते हैं, और हम उसी परिणाम को प्राप्त करने के लिए उपयोग प्रभाव हुक का उपयोग कर सकते हैं।

स्पष्ट विधि, जो ध्यान में आती है कि यूपीयू उपयोग कर सकता है

const [state, setState] = useState({ name: "Michael" })

useEffect(() => {
  console.log(state) // do something after state has updated
}, [state])

लेकिन यह पहले रेंडर पर भी फायर करेगा, इसलिए हम कोड को निम्न प्रकार से बदल सकते हैं जहां हम पहले रेंडर इवेंट की जांच कर सकते हैं और स्टेट रेंडर से बच सकते हैं। इसलिए कार्यान्वयन निम्नलिखित तरीके से किया जा सकता है:

हम पहले रेंडर की पहचान करने के लिए यहां उपयोगकर्ता हुक का उपयोग कर सकते हैं।

UseRef हुक हमें कार्यात्मक घटकों में उत्परिवर्तनीय चर बनाने की अनुमति देता है। यह डोम नोड्स / रिएक्ट तत्वों तक पहुँचने और पुन: रेंडर करने के लिए बिना परिवर्तनशील चर को स्टोर करने के लिए उपयोगी है।

const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);

useEffect(() => {
 if (!firstTimeRender.current) {
    console.log(state);
  }
}, [state])

useEffect(() => { 
  firstTimeRender.current = false 
}, [])

1

मैं एक ही समस्या में चल रहा था, मेरे सेटअप में useEffect का उपयोग कर चाल नहीं चला रहा था (मैं एक सरणी एकाधिक बाल घटकों से माता-पिता की स्थिति को अपडेट कर रहा हूं और मुझे यह जानने की आवश्यकता है कि किस घटक ने डेटा अपडेट किया है)।

वादे में सेटस्टैट लपेटकर पूरा होने के बाद एक मनमाना कार्रवाई करने की अनुमति देता है:

import React, {useState} from 'react'

function App() {
  const [count, setCount] = useState(0)

  function handleClick(){
    Promise.resolve()
      .then(() => { setCount(count => count+1)})
      .then(() => console.log(count))
  }


  return (
    <button onClick= {handleClick}> Increase counter </button>
  )
}

export default App;

निम्नलिखित प्रश्न ने मुझे सही दिशा में लगाया: क्या हुक का उपयोग करते समय रिएक्ट बैच स्थिति अद्यतन करता है?


0

आपका प्रश्न बहुत ही मान्य है। मुझे बताएं कि उपयोग डिफ़ॉल्ट रूप से एक बार चलता है और हर बार निर्भरता सरणी में परिवर्तन होता है।

नीचे दिए गए उदाहरण की जाँच करें ::

import React,{ useEffect, useState } from "react";

const App = () => {
  const [age, setAge] = useState(0);
  const [ageFlag, setAgeFlag] = useState(false);

  const updateAge = ()=>{
    setAgeFlag(false);
    setAge(age+1);
    setAgeFlag(true);
  };

  useEffect(() => {
    if(!ageFlag){
      console.log('effect called without change - by default');
    }
    else{
      console.log('effect called with change ');
    }
  }, [ageFlag,age]);

  return (
    <form>
      <h2>hooks demo effect.....</h2>
      {age}
      <button onClick={updateAge}>Text</button>
    </form>
  );
}

export default App;

यदि आप चाहते हैं कि सेटस्टैट कॉलबैक को हुक के साथ निष्पादित किया जाए, तो फ्लैग वेरिएबल का उपयोग करें और IFESE या IF ब्लॉक को उपयोग के अंदर दें, ताकि जब स्थिति संतुष्ट हो जाए, तभी उस कोड ब्लॉक को निष्पादित करें। किसी भी समय प्रभाव निर्भरता सरणी परिवर्तन के रूप में चलता है लेकिन यह कि प्रभाव के अंदर IF कोड केवल उस विशिष्ट स्थितियों पर ही अमल करेगा।


4
यह काम नहीं करेगा। आप नहीं जानते कि अपडेट के अंदर तीन कथन वास्तव में किस क्रम में काम करेंगे। तीनों अस्सिटेंट हैं। केवल गारंटी की बात यह है कि पहली पंक्ति 3 से पहले चलती है (क्योंकि वे एक ही राज्य पर काम करते हैं)। आपको 2nd लाइन के बारे में कुछ भी पता नहीं है। यह देखने के लिए यह उदाहरण बहुत अधिक अनुकरणीय है।
मोहित सिंह

मेरे दोस्त मोहित मैंने इस तकनीक को एक बड़े जटिल प्रतिक्रिया परियोजना में लागू किया है जब मैं प्रतिक्रिया कक्षाओं से हुक तक जा रहा था और यह पूरी तरह से काम करता है। बस सेटबैक कॉलबैक को बदलने के लिए हुक में कहीं भी एक ही तर्क की कोशिश करें और आपको पता चल जाएगा।
अर्जुन साह

4
"मेरी परियोजना में काम एक स्पष्टीकरण नहीं है", डॉक्स पढ़ें। वे समकालिक नहीं हैं। आप यह सुनिश्चित करने के लिए नहीं कह सकते हैं कि अद्यतन में तीन लाइनें उस क्रम में काम करेंगी। यदि यह सिंक में था, तो ध्वज की आवश्यकता है, सीधे सेट.लग के बाद कंसोल (सीधे) कॉल करें।
मोहित सिंह

useRef "ageFlag" के लिए एक बेहतर उपाय है।
Dr.Flink

0

मेरे पास एक उपयोग का मामला था जहां मैं राज्य के सेट होने के बाद कुछ परम के साथ एक एपीआई कॉल करना चाहता था । मैं उन पारमों को अपने राज्य के रूप में स्थापित नहीं करना चाहता था इसलिए मैंने एक कस्टम हुक बनाया और यहाँ मेरा समाधान है

import { useState, useCallback, useRef, useEffect } from 'react';
import _isFunction from 'lodash/isFunction';
import _noop from 'lodash/noop';

export const useStateWithCallback = initialState => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(_noop);

  const handleStateChange = useCallback((updatedState, callback) => {
    setState(updatedState);
    if (_isFunction(callback)) callbackRef.current = callback;
  }, []);

  useEffect(() => {
    callbackRef.current();
    callbackRef.current = _noop; // to clear the callback after it is executed
  }, [state]);

  return [state, handleStateChange];
};

-3

UseEffect प्राथमिक समाधान है। लेकिन जैसा कि डैरिल ने उल्लेख किया है, उपयोग में आने वाला उपयोग और राज्य में पास होने के रूप में दूसरे पैरामीटर में एक दोष है, घटक प्रारंभिक प्रक्रिया पर चलेगा। यदि आप चाहते हैं कि कॉलबैक फ़ंक्शन अपडेटेड स्टेटस वैल्यू का उपयोग करके चले, तो आप एक स्थानीय स्थिरांक सेट कर सकते हैं और सेटस्टैट और कॉलबैक दोनों में इसका उपयोग कर सकते हैं।

const [counter, setCounter] = useState(0);

const doSomething = () => {
  const updatedNumber = 123;
  setCounter(updatedNumber);

  // now you can "do something" with updatedNumber and don't have to worry about the async nature of setState!
  console.log(updatedNumber);
}

5
setCounter async है अगर आप नहीं जानते कि क्या सांत्वना है। अगर setCounter के बाद या उससे पहले कॉल किया जाएगा।
मोहित सिंह
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.