उपयोग में Async फ़ंक्शन के लिए प्रतिक्रिया हुक चेतावनी: उपयोग फ़ंक्शन फ़ंक्शन को क्लीनअप फ़ंक्शन या कुछ भी वापस नहीं करना चाहिए


119

मैं नीचे दिए गए कुछ उदाहरण का उपयोग करने की कोशिश कर रहा था:

useEffect(async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}, []);

और मुझे यह चेतावनी मेरे कंसोल में मिलती है। लेकिन मुझे लगता है कि async कॉल के लिए क्लीनअप वैकल्पिक है। मुझे यकीन नहीं है कि मुझे यह चेतावनी क्यों मिली है। उदाहरण के लिए सैंडबॉक्स को जोड़ना। https://codesandbox.io/s/24rj871r0p यहां छवि विवरण दर्ज करें

जवाबों:


166

मैं दान Abramov (प्रतिक्रिया कोर अनुरक्षकों में से एक) को यहां देखने का सुझाव देता हूं :

मुझे लगता है कि आप इसे ज़रूरत से ज़्यादा जटिल बना रहे हैं।

function Example() {
  const [data, dataSet] = useState<any>(null)

  useEffect(() => {
    async function fetchMyAPI() {
      let response = await fetch('api/data')
      response = await response.json()
      dataSet(response)
    }

    fetchMyAPI()
  }, [])

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

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

const response = MyAPIResource.read();

और कोई प्रभाव नहीं। लेकिन इस बीच आप एसिंक्स सामान को एक अलग फ़ंक्शन में स्थानांतरित कर सकते हैं और इसे कॉल कर सकते हैं।

आप प्रायोगिक रहस्य के बारे में अधिक पढ़ सकते हैं ।


यदि आप एस्किल्ट के साथ बाहर के कार्यों का उपयोग करना चाहते हैं।

 function OutsideUsageExample() {
  const [data, dataSet] = useState<any>(null)

  const fetchMyAPI = useCallback(async () => {
    let response = await fetch('api/data')
    response = await response.json()
    dataSet(response)
  }, [])

  useEffect(() => {
    fetchMyAPI()
  }, [fetchMyAPI])

  return (
    <div>
      <div>data: {JSON.stringify(data)}</div>
      <div>
        <button onClick={fetchMyAPI}>manual fetch</button>
      </div>
    </div>
  )
}

UseCallback साथ useCallbackसैंडबॉक्स

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

export default function App() {
  const [counter, setCounter] = useState(1);

  // if counter is changed, than fn will be updated with new counter value
  const fn = useCallback(() => {
    setCounter(counter + 1);
  }, [counter]);

  // if counter is changed, than fn will not be updated and counter will be always 1 inside fn
  /*const fnBad = useCallback(() => {
      setCounter(counter + 1);
    }, []);*/

  // if fn or counter is changed, than useEffect will rerun
  useEffect(() => {
    if (!(counter % 2)) return; // this will stop the loop if counter is not even

    fn();
  }, [fn, counter]);

  // this will be infinite loop because fn is always changing with new counter value
  /*useEffect(() => {
    fn();
  }, [fn]);*/

  return (
    <div>
      <div>Counter is {counter}</div>
      <button onClick={fn}>add +1 count</button>
    </div>
  );
}

आप घटक की तरह अनमाउंट है तो जाँच करके दौड़ की स्थिति के मुद्दों को हल कर सकते हैं: useEffect(() => { let unmounted = false promise.then(res => { if (!unmounted) { setState(...) } }) return () => { unmounted = true } }, [])
रिचर्ड

1
आप उपयोग- async-effect नामक पैकेज का भी उपयोग कर सकते हैं । यह पैकेज आपको एसिंक्स वेट सिंटैक्स का उपयोग करने में सक्षम बनाता है।
किटकैट

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

1
अरे, यह ठीक है और मैं ज्यादातर समय क्या करता हूं। लेकिन eslintमुझे इसके लिए fetchMyAPI()निर्भरता के रूप में बनाने के लिए कहता हैuseEffect
प्रकाश रेड्डी पोटलापाडु

51

जब आप एक async फ़ंक्शन का उपयोग करते हैं

async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}

यह एक वादा वापस करता है और useEffectकॉलबैक फ़ंक्शन को वादा वापस करने की उम्मीद नहीं करता है, बल्कि यह उम्मीद करता है कि कुछ भी नहीं लौटाया गया है या कोई फ़ंक्शन वापस किया गया है।

चेतावनी के लिए एक वर्कअराउंड के रूप में आप async फ़ंक्शन का उपयोग कर सकते हैं।

useEffect(() => {
    (async function() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    })();
}, []);

या इसे और अधिक स्वच्छ बनाने के लिए आप किसी फ़ंक्शन को परिभाषित कर सकते हैं और फिर उसे कॉल कर सकते हैं

useEffect(() => {
    async function fetchData() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    };
    fetchData();
}, []);

दूसरा समाधान पढ़ने में आसान बना देगा और आपको पिछले अनुरोधों को रद्द करने के लिए कोड लिखने में मदद करेगा यदि कोई नया निकाल दिया जाता है या राज्य में नवीनतम अनुरोध को सहेज लिया जाता है

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


इसे आसान बनाने के लिए एक पैकेज बनाया गया है। आप इसे यहाँ पा सकते हैं ।
किटकैट

1
लेकिन एश्लिन उस के साथ बर्दाश्त नहीं करेगा
मुहिम सीएस

1
क्लीनअप / डिमाउंट कॉलबैक निष्पादित करने का कोई तरीका नहीं है
डेविड रियर

1
@ ShubhamKhatri जब आप उपयोग करते हैं, तो आप useEffectकिसी फ़ंक्शन को साफ़ कर सकते हैं जैसे कि घटनाओं की सदस्यता समाप्त करना। जब आप async फ़ंक्शन का उपयोग करते हैं, तो आप कुछ भी वापस नहीं कर सकते क्योंकि useEffectपरिणाम का इंतजार नहीं करेंगे
डेविड रियर

2
क्या आप कह रहे हैं कि मैं एक async एक में एक सफाई समारोह रख सकते हैं? मैंने कोशिश की लेकिन मेरा क्लीन अप फंक्शन सिर्फ कभी नहीं कहा जाता है। क्या आप थोड़ा उदाहरण बना सकते हैं?
डेविड रिअरेट

32

जब तक रिएक्ट एक बेहतर तरीका प्रदान नहीं करता, तब तक आप एक सहायक बना सकते हैं useEffectAsync.js:

import { useEffect } from 'react';


export default function useEffectAsync(effect, inputs) {
    useEffect(() => {
        effect();
    }, inputs);
}

अब आप एक async फ़ंक्शन पास कर सकते हैं:

useEffectAsync(async () => {
    const items = await fetchSomeItems();
    console.log(items);
}, []);

8
कारण React स्वचालित रूप से उपयोग में Async फ़ंक्शन की अनुमति नहीं देता है। यह है कि मामलों के एक बड़े हिस्से में, कुछ सफाई आवश्यक है। समारोह useAsyncEffectके रूप में आपके द्वारा लिखी गई यह सोच में आसानी से गुमराह कोई अगर वे एक सफाई समारोह उनके async प्रभाव यह उचित समय पर चलाने की जाएगी से लौट सकते हैं। यह मेमोरी लीक या बदतर बग पैदा कर सकता है, इसलिए हमने लोगों को रिएक्ट के जीवनचक्र के साथ संपर्क करने वाले एसिंक्स कार्यों के "सीम" को बनाने के लिए अपने कोड को फिर से भरने के लिए प्रोत्साहित करने का विकल्प चुना, और परिणामस्वरूप कोड का व्यवहार अधिक जानबूझकर और सही हो गया।
सोफी अल्परट

8

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

const [data, setData] = useState(null);

/* This would be called on initial page load */
useEffect(()=>{
    fetch(`https://www.reddit.com/r/${subreddit}.json`)
    .then(data => {
        setData(data);
    })
    .catch(err => {
        /* perform error handling if desired */
    });
}, [])

/* This would be called when store/state data is updated */
useEffect(()=>{
    if (data) {
        setPosts(data.children.map(it => {
            /* do what you want */
        }));
    }
}, [data]);

संदर्भ => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects


2

शून्य ऑपरेटर का उपयोग यहां किया जा सकता है।
के बजाय:

React.useEffect(() => {
    async function fetchData() {
    }
    fetchData();
}, []);

या

React.useEffect(() => {
    (async function fetchData() {
    })()
}, []);

आप लिख सकते हैं:

React.useEffect(() => {
    void async function fetchData() {
    }();
}, []);

यह थोड़ा क्लीनर और प्रीटियर है।


Async प्रभाव मेमोरी लीक का कारण बन सकता है इसलिए घटक अनमाउंट पर सफाई करना महत्वपूर्ण है। इस मामले में यह इस तरह दिख सकता है:

function App() {
    const [ data, setData ] = React.useState([]);

    React.useEffect(() => {
        const abortController = new AbortController();
        void async function fetchData() {
            try {
                const url = 'https://jsonplaceholder.typicode.com/todos/1';
                const response = await fetch(url, { signal: abortController.signal });
                setData(await response.json());
            } catch (error) {
                console.log('error', error);
            }
        }();
        return () => {
            abortController.abort(); // cancel pending fetch request on component unmount
        };
    }, []);

    return <pre>{JSON.stringify(data, null, 2)}</pre>;
}


1

अन्य पाठकों के लिए, त्रुटि इस तथ्य से आ सकती है कि एसिंक्स फ़ंक्शन को लपेटने वाला कोई ब्रैकेट नहीं है:

Async फ़ंक्शन initData को ध्यान में रखते हुए

  async function initData() {
  }

यह कोड आपकी त्रुटि का कारण बनेगा:

  useEffect(() => initData(), []);

लेकिन यह एक, नहीं होगा:

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

(ईटडाटा के आसपास के कोष्ठकों पर ध्यान दें) ()


1
शानदार, यार! मैं गाथा का उपयोग कर रहा हूं, और यह त्रुटि तब दिखाई दी जब मैं एक एक्शन क्रिएटर को बुला रहा था जिसने एकमात्र ऑब्जेक्ट वापस कर दिया। यह उपयोग की तरह दिखता है कॉलबैक फ़ंक्शन इस व्यवहार को नहीं चाटता है। मैं आपके उत्तर की सराहना करता हूं।
गोर्र

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