उपयोग में अनंत लूप


115

मैं रिएक्ट 16.7-अल्फा में नए हुक सिस्टम के साथ खेल रहा हूं और एक अनंत लूप में फंस जाता हूं जब मैं जिस राज्य को संभाल रहा हूं वह ऑब्जेक्ट या एरे है।

सबसे पहले, मैं useState का उपयोग करता हूं और इसे इस तरह एक खाली वस्तु के साथ आरंभ करता हूं:

const [obj, setObj] = useState({});

फिर, useEffect में, मैं setObj का उपयोग करके इसे फिर से किसी खाली वस्तु पर सेट करता हूं। एक दूसरे तर्क के रूप में मैं [obj] पास कर रहा हूं, उम्मीद करता हूं कि अगर सामग्री की सामग्री नहीं बदली है तो यह अपडेट नहीं करेगा। लेकिन यह अपडेट होता रहता है। मुझे लगता है कि कोई फर्क नहीं पड़ता क्योंकि सामग्री, ये हमेशा अलग-अलग वस्तुएं हैं जो रिएक्ट सोच रही हैं कि यह बदलती रहती है?

useEffect(() => {
  setIngredients({});
}, [ingredients]);

सरणियों के साथ भी यही सच है, लेकिन एक आदिम के रूप में यह एक पाश में फंस नहीं सकता है, जैसा कि अपेक्षित था।

इन नए हुक का उपयोग करते हुए, मौसम की सामग्री को बदलते समय या नहीं जाँचते समय मुझे वस्तुओं और सरणी को कैसे संभालना चाहिए?


टोबियास, क्या उपयोग के मामले में आउटपुट के मूल्य को बदलने की आवश्यकता होती है, एक बार जब यह मूल्य बदल जाता है?
बेन कार्प ने

@ टोबियास, आपको मेरा जवाब पढ़ना चाहिए। मुझे यकीन है कि आप इसे सही उत्तर के रूप में स्वीकार करेंगे।
हाफवेबडेव

जवाबों:


128

उपयोग करने के लिए दूसरे तर्क के रूप में एक खाली सरणी पास करना यह केवल माउंट और अनमाउंट पर चलता है, इस प्रकार किसी भी अनंत छोरों को रोकता है।

useEffect(() => {
  setIngredients({});
}, []);

यह मुझे https://www.robinwieruch.de/react-nook.com/ पर रिएक्ट हुक पर ब्लॉग पोस्ट में स्पष्ट किया गया था


1
यह वास्तव में एक खाली सरणी है जो आपको एक खाली वस्तु नहीं चाहिए, जिसमें आपको पास होना चाहिए।
GifCo

20
यह समस्या को हल नहीं करता है, आपको हुक द्वारा उपयोग की जाने वाली निर्भरताएं पास करने की आवश्यकता है
19

1
ध्यान दें कि यदि आपने एक निर्दिष्ट किया है, तो प्रभाव को साफ करने पर क्लीनअप फ़ंक्शन चलेगा। वास्तविक प्रभाव अनमाउंट पर नहीं चलता है। reactjs.org/docs/hooks-effect.html#example-use-hooks-1
tony

2
डैन अब्रामोव ने कहा कि खाली सरणी का उपयोग करते समय सेटइन्ग्रिडिएट्स एक निर्भरता है। एक घटक की तरह उपयोग का इलाज न करेंडिमाउंट () विधि
p7adams

1
के बाद से लोगों के ऊपर कैसे सही ढंग से इस समस्या को हल करने के लिए पर एक लिंक या संसाधन नहीं प्रदान की थी, मैं इस बाहर की जाँच करने के लिए आनेवाला duckduckgoers सलाह देते हैं क्योंकि यह मेरी अनंत लूप समस्या तय: stackoverflow.com/questions/56657907/...
सी रिब

78

एक ही समस्या थी। मुझे नहीं पता कि उन्होंने डॉक्स में इसका उल्लेख क्यों किया है। बस टोबियास हौगेन जवाब में थोड़ा जोड़ना चाहते हैं।

प्रत्येक घटक / मूल रेंडरर में चलाने के लिए आपको उपयोग करने की आवश्यकता है:

  useEffect(() => {

    // don't know where it can be used :/
  })

घटक माउंट के बाद केवल एक बार कुछ भी चलाने के लिए (एक बार प्रदान किया जाएगा) आपको उपयोग करने की आवश्यकता है:

  useEffect(() => {

    // do anything only one time if you pass empty array []
    // keep in mind, that component will be rendered one time (with default values) before we get here
  }, [] )

घटक माउंट और डेटा / डेटा 2 परिवर्तन पर एक बार कुछ भी चलाने के लिए :

  const [data, setData] = useState(false)
  const [data2, setData2] = useState('default value for first render')
  useEffect(() => {

// if you pass some variable, than component will rerender after component mount one time and second time if this(in my case data or data2) is changed
// if your data is object and you want to trigger this when property of object changed, clone object like this let clone = JSON.parse(JSON.stringify(data)), change it clone.prop = 2 and setData(clone).
// if you do like this 'data.prop=2' without cloning useEffect will not be triggered, because link to data object in momory doesn't changed, even if object changed (as i understand this)
  }, [data, data2] )

मैं इसका ज्यादातर समय कैसे उपयोग करता हूं:

export default function Book({id}) { 
  const [book, bookSet] = useState(false) 

  useEffect(() => {
    loadBookFromServer()
  }, [id]) // every time id changed, new book will be loaded

  // Remeber that it's not always safe to omit functions from the list of dependencies. Explained in comments.
  async function loadBookFromServer() {
    let response = await fetch('api/book/' + id)
    response  = await response.json() 
    bookSet(response)
  }

  if (!book) return false //first render, when useEffect did't triggered yet we will return false

  return <div>{JSON.stringify(book)}</div>  
}


@endavid इस बात पर निर्भर करता है कि आप क्या उपयोग कर रहे हैं
ZiiMakc

1
बेशक, यदि आप घटक दायरे से किसी भी मान का उपयोग नहीं करते हैं , तो यह सुरक्षित है। लेकिन विशुद्ध रूप से डिजाइनिंग / वास्तु के दृष्टिकोण से, यह एक अच्छा अभ्यास नहीं है, क्योंकि इसके लिए आपको प्रभाव के अंदर अपने पूरे कार्य को स्थानांतरित करने की आवश्यकता होती है, यदि आपको प्रॉपर का उपयोग करने की आवश्यकता है और आप एक उपयोगी विधि के साथ एंडअप कर सकते हैं जो कि उपयोग कर रहा है कोड की लाइनों की अपमानजनक राशि।
egdavid

18

मैं भी एक ही समस्या में भाग गया और मैंने यह सुनिश्चित करके कि मैंने दूसरे तर्क में आदिम मूल्यों को पारित किया है []

यदि आप एक ऑब्जेक्ट पास करते हैं, तो रिएक्ट केवल ऑब्जेक्ट को रेफरेंस स्टोर करेगा और रेफ़रेंस में बदलाव होने पर इफ़ेक्ट को चलाएगा, जो कि आमतौर पर हर सिंगे टाइम होता है (मैं अब हालांकि कैसे नहीं)।

समाधान वस्तु में मूल्यों को पारित करना है। तुम कोशिश कर सकते हो,

const obj = { keyA: 'a', keyB: 'b' }

useEffect(() => {
  // do something
}, [Object.values(obj)]);

या

const obj = { keyA: 'a', keyB: 'b' }

useEffect(() => {
  // do something
}, [obj.keyA, obj.keyB]);

4
एक अन्य दृष्टिकोण है कि यूज़मेमो के साथ ऐसे मान बनाएं, जिस तरह से संदर्भ रखा जाता है और निर्भरता के मूल्यों का मूल्यांकन उसी के रूप में किया जाता है
हेलो

@helado आप मान या useCallback () कार्यों के लिए के लिए useMemo () का उपयोग कर सकते
Juanma मेनेंडेज़

क्या वह यादृच्छिक मान निर्भरता सरणी में पास किया जा रहा है? बेशक यह अनंत लूप को रोकता है लेकिन क्या इसे प्रोत्साहित किया जाता है? मैं 0निर्भरता सरणी के लिए भी पारित कर सकता है ?
थिंकटेंटेडड्यू

11

जैसा कि प्रलेखन में कहा गया है ( https://reactjs.org/docs/hooks-effect.html ), useEffectहुक का उपयोग तब किया जाता है जब आप चाहते हैं कि हर रेंडर के बाद कुछ कोड निष्पादित किया जाए । डॉक्स से:

हर रेंडर के बाद useEffect चलता है? हाँ!

यदि आप इसे कस्टमाइज़ करना चाहते हैं, तो आप बाद में दिखाई देने वाले निर्देशों का पालन कर सकते हैं ( https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects )। मूल रूप से, useEffectविधि एक दूसरे तर्क को स्वीकार करती है , कि रिएक्ट यह निर्धारित करने के लिए जांच करेगा कि क्या प्रभाव को फिर से चालू करना है या नहीं।

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

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


11

यदि आप एक कस्टम हुक का निर्माण कर रहे हैं , तो आप कभी-कभी डिफ़ॉल्ट के साथ अनंत लूप का कारण बन सकते हैं

function useMyBadHook(values = {}) {
    useEffect(()=> { 
           /* This runs every render, if values is undefined */
        },
        [values] 
    )
}

फ़िक्स प्रत्येक फ़ंक्शन कॉल पर एक नया बनाने के बजाय उसी ऑब्जेक्ट का उपयोग करना है:

const defaultValues = {};
function useMyBadHook(values = defaultValues) {
    useEffect(()=> { 
           /* This runs on first call and when values change */
        },
        [values] 
    )
}

यदि आप अपने कंपोनेंट कोड में इसका सामना कर रहे हैं तो एलएस 6 डिफॉल्ट वैल्यू के बजाय डिफॉल्टप्रॉप्स का उपयोग करने पर लूप ठीक हो सकता है

function MyComponent({values}) {
  useEffect(()=> { 
       /* do stuff*/
    },[values] 
  )
  return null; /* stuff */
}

MyComponent.defaultProps = {
  values = {}
}

धन्यवाद! यह मेरे लिए गैर-स्पष्ट था और वास्तव में मैं जिस समस्या में चल रहा था।
अटक गया

1
आपने मेरा दिन बचाया! धन्यवाद भाई।
हान वान फाम

7

मुझे यकीन नहीं है कि यह आपके लिए काम करेगा लेकिन आप इसे जोड़ने की कोशिश कर सकते हैं। इस तरह से गति:

useEffect(() => {
        // fetch from server and set as obj
}, [obj.length]);

मेरे मामले में (मैं एक सरणी ला रहा था!) ​​यह माउंट पर डेटा लाया, फिर केवल परिवर्तन पर और यह लूप में नहीं गया।


1
यदि किसी वस्तु को सरणी में बदल दिया जाए तो क्या होगा? उस स्थिति में सरणी की लंबाई समान होगी और प्रभाव नहीं चलेगा!
आमिर खान

7

आप के अंत में खाली सरणी शामिल करते हैं तो useEffect :

useEffect(()=>{
        setText(text);
},[])

यह एक बार चलेगा।

यदि आप सरणी पर पैरामीटर भी शामिल करते हैं:

useEffect(()=>{
            setText(text);
},[text])

जब भी पाठ पैरामीटर बदलता है तो यह चलता है।


यदि हम एक हुक के अंत में एक खाली सरणी डालते हैं तो यह केवल एक बार क्यों चलेगा?
करेन

एक यूज़फेक के अंत में एक खाली सरणी डेवलपर्स द्वारा उन स्थितियों में अनंत छोरों को रोकने के लिए एक उद्देश्यपूर्ण कार्यान्वयन है जहां आप उदाहरण के लिए, उपयोग के अंदर सेट करने की आवश्यकता कर सकते हैं। यह अन्यथा उपयोग करने की ओर ले जाता है -> राज्य का अद्यतन -> उपयोग का लाभ -> अनंत लूप।
240

खाली सरणी एस्केलेटर से चेतावनी का कारण बनती है।
hmcclungiii

4

आपका अनंत लूप वृत्ताकारता के कारण है

useEffect(() => {
  setIngredients({});
}, [ingredients]);

setIngredients({});का मान बदल जाएगा ingredients(प्रत्येक बार एक नया संदर्भ लौटाएगा), जो चलेगा setIngredients({})। इसे हल करने के लिए आप या तो दृष्टिकोण का उपयोग कर सकते हैं:

  1. उपयोग करने के लिए एक अलग दूसरा तर्क पास करें
const timeToChangeIngrediants = .....
useEffect(() => {
  setIngredients({});
}, [timeToChangeIngrediants ]);

setIngrediantstimeToChangeIngrediantsबदल जाएगा जब बदल जाएगा।

  1. मुझे यकीन नहीं है कि एक बार इसे बदल दिए जाने के बाद केस का उपयोग किस तरह से बदलाव के प्रतिमानों को सही ठहराता है। लेकिन अगर यह मामला है, तो आप उपयोग करने Object.values(ingrediants)के लिए दूसरे तर्क के रूप में पास होते हैं।
useEffect(() => {
  setIngredients({});
}, Object.values(ingrediants));

2

यदि आप इस अनुकूलन का उपयोग करते हैं, तो सुनिश्चित करें कि सरणी में घटक स्कोप (जैसे प्रॉप्स और स्टेट) से सभी मान शामिल हैं जो समय के साथ बदलते हैं और जिनका उपयोग प्रभाव द्वारा किया जाता है।

मेरा मानना ​​है कि वे इस संभावना को व्यक्त करने की कोशिश कर रहे हैं कि कोई बासी डेटा का उपयोग कर सकता है, और इस बारे में जागरूक हो सकता है। यह मायने नहीं रखता कि हम arrayदूसरे तर्क के लिए कब तक भेजते हैं क्योंकि हम जानते हैं कि यदि उन मूल्यों में से कोई भी परिवर्तन होता है तो यह प्रभाव को क्रियान्वित करेगा। यदि हम ingredientsप्रभाव के भीतर गणना के भाग के रूप में उपयोग कर रहे हैं , तो हमें इसे इसमें शामिल करना चाहिए array

const [ingredients, setIngredients] = useState({});

// This will be an infinite loop, because by shallow comparison ingredients !== {} 
useEffect(() => {
  setIngredients({});
}, [ingredients]);

// If we need to update ingredients then we need to manually confirm 
// that it is actually different by deep comparison.

useEffect(() => {
  if (is(<similar_object>, ingredients) {
    return;
  }
  setIngredients(<similar_object>);
}, [ingredients]);


1

सबसे अच्छा तरीका है का उपयोग करके वर्तमान मूल्य के साथ पिछले मूल्य की तुलना करने के लिए है usePrevious () और _.isEqual () से Lodash । आयात isEqual और useRef । उपयोग के अंदर अपने मौजूदा मूल्य की वर्तमान मूल्य के साथ तुलना करें () । यदि वे समान हैं तो कुछ और अपडेट नहीं करें। यूज़प्रिफ़र (मान) एक कस्टम हुक है जो यूज़फ्रेम () के साथ एक रेफरी बनाता है

नीचे मेरे कोड का स्निपेट है। मुझे फायरबेस हुक का उपयोग करके डेटा अपडेट करने के साथ अनंत लूप की समस्या का सामना करना पड़ रहा था

import React, { useState, useEffect, useRef } from 'react'
import 'firebase/database'
import { Redirect } from 'react-router-dom'
import { isEqual } from 'lodash'
import {
  useUserStatistics
} from '../../hooks/firebase-hooks'

export function TMDPage({ match, history, location }) {
  const usePrevious = value => {
    const ref = useRef()
    useEffect(() => {
      ref.current = value
    })
    return ref.current
  }
  const userId = match.params ? match.params.id : ''
  const teamId = location.state ? location.state.teamId : ''
  const [userStatistics] = useUserStatistics(userId, teamId)
  const previousUserStatistics = usePrevious(userStatistics)

  useEffect(() => {
      if (
        !isEqual(userStatistics, previousUserStatistics)
      ) {
        
        doSomething()
      }
     
  })


1
मुझे लगता है कि लोगों ने इसे अस्वीकार कर दिया क्योंकि आपने तीसरे पक्ष के पुस्तकालय का उपयोग करने का सुझाव दिया था। हालांकि, अंतर्निहित विचार अच्छा है।
jperl

1

मामले में आपको ऑब्जेक्ट की तुलना करने की आवश्यकता होती है और जब इसे अपडेट किया जाता है तो यह deepCompareतुलना के लिए एक हुक है। स्वीकार किए गए उत्तर निश्चित रूप से संबोधित नहीं करते हैं। एक []सरणी होने के लिए उपयुक्त है यदि आपको केवल एक बार चलने पर प्रभाव चलाने की आवश्यकता है।

इसके अलावा, अन्य मत वाले उत्तर केवल आदिम प्रकारों के लिए एक चेक को संबोधित करते हैं obj.valueया कुछ ऐसा ही करते हैं जो पहले उस स्तर तक पहुंचते हैं जहां यह घोंसला नहीं है। यह गहरी नेस्टेड वस्तुओं के लिए सबसे अच्छा मामला नहीं हो सकता है।

तो यहाँ एक है जो सभी मामलों में काम करेगा।

import { DependencyList } from "react";

const useDeepCompare = (
    value: DependencyList | undefined
): DependencyList | undefined => {
    const ref = useRef<DependencyList | undefined>();
    if (!isEqual(ref.current, value)) {
        ref.current = value;
    }
    return ref.current;
};

आप useEffectहुक में इसका उपयोग कर सकते हैं

React.useEffect(() => {
        setState(state);
    }, useDeepCompare([state]));

0

आप निर्भरता सरणी में ऑब्जेक्ट को भी नष्ट कर सकते हैं, जिसका अर्थ है कि राज्य केवल तब अपडेट करेगा जब ऑब्जेक्ट के कुछ हिस्से अपडेट किए जाएंगे।

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

फिर आप इसे आगे ले जा सकते हैं और केवल कुछ बिंदुओं पर गाजर की संख्या को अपडेट कर सकते हैं, इस प्रकार नियंत्रित कर सकते हैं कि राज्य कब अपडेट होगा और एक अनंत लूप से बचना चाहिए।

useEffect(() => {
  setIngredients({});
}, [ingredients.carrots]);

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


0

मेरा मामला एक अनंत लूप का सामना करने पर विशेष था, सेनारियो इस तरह था:

मेरे पास एक ऑब्जेक्ट था, जो कहता है कि objX जो प्रॉप्स से आता है और मैं इसे प्रॉप्स में नष्ट कर रहा हूं जैसे:

const { something: { somePropery } } = ObjX

और मैंने someProperyअपनी useEffectपसंद पर निर्भरता के रूप में इस्तेमाल किया :


useEffect(() => {
  // ...
}, [somePropery])

और इसने मुझे एक अनंत लूप दिया, मैंने somethingइसे एक निर्भरता के रूप में पूरा करके इसे संभालने की कोशिश की और इसने ठीक से काम किया।

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