useEffect - स्टेट अपडेट करते समय अनंत लूप को रोकें


9

मैं चाहूंगा कि उपयोगकर्ता टूडू आइटम की एक सूची को सॉर्ट करने में सक्षम हो। जब उपयोगकर्ता ड्रॉपडाउन से किसी आइटम का चयन करता है, तो यह सेट हो जाएगा, sortKeyजो नए संस्करण का निर्माण करेगा setSortedTodosऔर बदले में useEffectकॉल और कॉल को ट्रिगर करेगा setSortedTodos

नीचे दिए गए उदाहरण ठीक उसी तरह से काम करते हैं जैसे मैं कैसे चाहता हूं, हालांकि एस्लिंट मुझे निर्भरता सरणी में जोड़ने के todosलिए प्रेरित कर useEffectरहा है, और अगर मैं ऐसा करता हूं तो यह एक अनंत लूप का कारण बनता है (जैसा कि आप उम्मीद करेंगे)।

const [todos, setTodos] = useState([]);
const [sortKey, setSortKey] = useState('title');

const setSortedTodos = useCallback((data) => {
  const cloned = data.slice(0);

  const sorted = cloned.sort((a, b) => {
    const v1 = a[sortKey].toLowerCase();
    const v2 = b[sortKey].toLowerCase();

    if (v1 < v2) {
      return -1;
    }

    if (v1 > v2) {
      return 1;
    }

    return 0;
  });

  setTodos(sorted);
}, [sortKey]);

useEffect(() => {
    setSortedTodos(todos);
}, [setSortedTodos]);

लाइव उदाहरण:

मुझे लगता है कि ऐसा करने का एक बेहतर तरीका होना चाहिए जो कि खुश रहना चाहता है।


1
बस एक ओर ध्यान दें: sortकॉलबैक सिर्फ हो सकता है: return a[sortKey].toLowerCase().localeCompare(b[sortKey].toLowerCase());जिसे लोकल की तुलना करने का फायदा है, अगर पर्यावरण के पास उचित स्थानीय जानकारी है। यदि आप चाहें, तो आप इस पर भी विनाश कर सकते हैं: pastebin.com/7X4M1XTH
TJ Crowder

क्या त्रुटि eslintफेंक रहा है?
लूज

क्या आप स्टैक स्निपेट्स ( टूलबार बटन) का उपयोग करके समस्या का एक रन योग्य न्यूनतम प्रतिलिपि प्रस्तुत करने योग्य उदाहरण प्रदान करने के लिए प्रश्न को अपडेट कर सकते हैं [<>]? जेएसएक्स सहित स्टैक स्निपेट्स रिएक्ट का समर्थन करते हैं; यहाँ एक कैसे करना है । इस तरह से लोग यह देख सकते हैं कि उनके प्रस्तावित समाधानों में अनंत लूप की समस्या नहीं है ...
टीजे क्राउडर

यह वास्तव में एक दिलचस्प दृष्टिकोण है, और वास्तव में एक दिलचस्प समस्या है। जैसा कि आप कहते हैं, आप समझ सकते हैं कि ESLint को क्यों लगता है कि आपको todosनिर्भरता सरणी में जोड़ने की आवश्यकता है useEffect, और आप देख सकते हैं कि आपको क्यों नहीं करना चाहिए। :-)
टीजे क्राउडर

मैंने आपके लिए लाइव उदाहरण जोड़ा। वास्तव में इसका उत्तर देखना चाहते हैं।
टीजे क्राउडर

जवाबों:


8

मेरा तर्क है कि इसका मतलब यह है कि इसके बारे में इस तरह से आदर्श नहीं है। फ़ंक्शन वास्तव में पर निर्भर है todos। यदि setTodosकहीं और कॉल किया जाता है, तो कॉलबैक फ़ंक्शन को फिर से विवादित करना पड़ता है, अन्यथा यह बासी डेटा पर काम करता है।

आप किसी भी तरह से राज्य में क्रमबद्ध सरणी को क्यों संग्रहीत करते हैं? आप useMemoकुंजी या सरणी में परिवर्तन होने पर मानों को क्रमबद्ध करने के लिए उपयोग कर सकते हैं :

const sortedTodos = useMemo(() => {
  return Array.from(todos).sort((a, b) => {
    const v1 = a[sortKey].toLowerCase();
    const v2 = b[sortKey].toLowerCase();

    if (v1 < v2) {
      return -1;
    }

    if (v1 > v2) {
      return 1;
    }

    return 0;
  });
}, [sortKey, todos]);

फिर sortedTodosहर जगह संदर्भ ।

लाइव उदाहरण:

राज्य में सॉर्ट किए गए मानों को संग्रहीत करने की आवश्यकता नहीं है, क्योंकि आप "आधार" सरणी और सॉर्ट कुंजी से सॉर्ट किए गए सरणी को हमेशा प्राप्त / गणना कर सकते हैं। मेरा तर्क है कि यह आपके कोड को समझने में आसान बनाता है क्योंकि यह कम जटिल है।


के ओह अच्छा उपयोग useMemo। बस एक पक्ष-प्रश्न, .localCompareसॉर्ट में उपयोग क्यों नहीं ?
टिक

2
यह वास्तव में बेहतर होगा। मैंने सिर्फ ओपी के कोड की नकल की और उस पर ध्यान नहीं दिया (वास्तव में समस्या के लिए प्रासंगिक नहीं)।
फेलिक्स क्लिंग

वास्तव में सरल, समाधान को समझना आसान है।
टीजे क्राउडर

आह हाँ, का उपयोग करें! उस बारे में भूल गए :)
DanV

3

अनंत लूप का कारण है क्योंकि टॉडोस पिछले संदर्भ से मेल नहीं खाता है और प्रभाव फिर से निकलेगा।

एक क्लिक-एक्शन के लिए किसी भी प्रभाव का उपयोग क्यों करें? आप इसे इस तरह से एक समारोह में चला सकते हैं:

const [todos, setTodos] = useState([]);

function sortTodos(e) {
    const sortKey = e.target.value;
    const clonedTodos = [...todos];
    const sorted = clonedTodos.sort((a, b) => {
        return a[sortKey.toLowerCase()].localeCompare(b[sortKey.toLowerCase()]);
    });

    setTodos(sorted);
}

और अपने ड्रॉपडाउन पर एक onChange

    <select onChange="sortTodos"> ......

जिस तरह से निर्भरता पर ध्यान दें, ESLint सही है! आपका टोडोस, ऊपर वर्णित मामले में, एक निर्भरता है और सूची में होना चाहिए। किसी आइटम के चयन पर दृष्टिकोण गलत है, और इसलिए आपकी समस्या है।


2
"सॉर्ट सरणी का एक नया उदाहरण लौटाएगा" बिल्ट-इन सॉर्ट विधि नहीं। यह सरणी को जगह में सॉर्ट करता है। data.slice(0)प्रतिलिपि बनाता है।
फेलिक्स क्लिंग

यह सच नहीं है जब आप ऐसा करते हैं setStateक्योंकि यह मौजूदा ऑब्जेक्ट को संपादित नहीं करेगा और इसके बाद आंतरिक रूप से इसे क्लोन करेगा। मेरे जवाब में गलत शब्द, सच है। मैं इसे संपादित करूंगा।
टिक

setStateक्लोन डेटा नहीं है। आप ऐसा क्यों सोचते हैं?
फेलिक्स क्लिंग

1
@Tikkes - नहीं, setStateकुछ भी क्लोन नहीं करता है। फेलिक्स और ओपी सही हैं, आपको इसे सॉर्ट करने से पहले सरणी को कॉपी करना होगा।
टीजे क्राउडर

ठीक है हाँ, क्षमा करें। मुझे लगता है कि यह आंतरिक पर कुछ और पढ़ने की जरूरत है। आपको वास्तव में मौजूदा को कॉपी और बदलना नहीं है state
टिक

0

कार्यात्मक रूप का उपयोग करने के लिए आपको यहां क्या करना है setState:

  const [todos, setTodos] = useState(exampleToDos);
    const [sortKey, setSortKey] = useState('title');

    const setSortedTodos = useCallback((data) => {

      setTodos(currTodos => {
        return currTodos.sort((a, b) => {
          const v1 = a[sortKey].toLowerCase();
          const v2 = b[sortKey].toLowerCase();

          if (v1 < v2) {
            return -1;
          }

          if (v1 > v2) {
            return 1;
          }

          return 0;
        });
      })

    }, [sortKey]);

    useEffect(() => {
        setSortedTodos(todos);
    }, [setSortedTodos, todos]);

काम कोडैंडबॉक्स

यहां तक ​​कि अगर आप राज्य को कॉपी कर रहे हैं तो मूल को म्यूट न करने के लिए, यह अभी भी गारंटी नहीं है कि राज्य को async स्थापित करने के कारण आपको इसका नवीनतम मूल्य मिलेगा। साथ ही अधिकांश विधियाँ एक उथली प्रति लौटाएंगी, जिससे आप मूल राज्य को किसी भी तरह से परिवर्तित कर सकते हैं।

कार्यात्मक का उपयोग करना setStateसुनिश्चित करता है कि आप राज्य का नवीनतम मूल्य प्राप्त करते हैं और मूल राज्य मूल्य को म्यूट नहीं करते हैं।


"और मूल राज्य मान को म्यूट न करें।" मुझे नहीं लगता कि रिएक्ट कॉलबैक के लिए एक प्रति पारित करता है.sortसरणी को जगह में बदलता है, इसलिए आपको अभी भी इसे स्वयं कॉपी करना होगा।
फेलिक्स क्लिंग

हम्म, अच्छी बात है, मुझे वास्तव में कोई पुष्टि नहीं मिल रही है कि यह राज्य की प्रतिलिपि पास करता है, हालांकि मुझे याद है कि पहले की तरह ही स्मथ पढ़ना।
क्लैरिटी

इसे यहां मिला: reactjs.org/docs/… । 'हम setState के कार्यात्मक अद्यतन रूप का उपयोग कर सकते हैं। यह हमें निर्दिष्ट करता है कि वर्तमान स्थिति को संदर्भित किए बिना राज्य को कैसे बदलना है '
स्पष्टता

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