यहाँ useFetch कोड मैंने बनाया है, जो इस विषय पर कई प्रसिद्ध लेखों पर आधारित है:

const dataFetchReducer = (state: any, action: any) => {
  let data, status, url;
  if (action.payload && action.payload.config) {
    ({ data, status } = action.payload);
    ({ url } = action.payload.config);
  }  

  switch (action.type) {
    case 'FETCH_INIT':
      return { 
        ...state, 
        isLoading: true, 
        isError: false 
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        isLoading: false,
        isError: false,
        data: data,
        status: status,
        url: url
      };
    case 'FETCH_FAILURE':
      return {
        ...state,
        isLoading: false,
        isError: true,
        data: null,
        status: status,
        url: url
      };
    default:
      throw new Error();
  }
}


/**
 * GET data from endpoints using AWS Access Token
 * @param {string} initialUrl   The full path of the endpoint to query
 * @param {JSON}   initialData  Used to initially populate 'data'
 */
export const useFetch = (initialUrl: ?string, initialData: any) => {
  const [url, setUrl] = useState<?string>(initialUrl);
  const { appStore } = useContext(AppContext);
  console.log('useFetch: url = ', url);
  const [state, dispatch] = useReducer(dataFetchReducer, {
    isLoading: false,
    isError: false,
    data: initialData,
    status: null,
    url: url
  });

  useEffect(() => {
    console.log('Starting useEffect in requests.useFetch', Date.now());
    let didCancel = false;
    const options = appStore.awsConfig;

    const fetchData = async () => {
      dispatch({ type: 'FETCH_INIT' });

      try {
        let response = {};
        if (url && options) {
          response = await axios.get(url, options);
        }

        if (!didCancel) {
          dispatch({ type: 'FETCH_SUCCESS', payload: response });
        }
      } catch (error) {
        // We won't force an error if there's no URL
        if (!didCancel && url !== null) {
          dispatch({ type: 'FETCH_FAILURE', payload: error.response });
        }
      }
    };

    fetchData();

    return () => {
      didCancel = true;
    };
  }, [url, appStore.awsConfig]);

  return [state, setUrl];
}

ऐसा लगता है कि एक उपयोग के मामले को छोड़कर ठीक काम करता है:

कल्पना करें कि एक नया ग्राहक नाम या उपयोगकर्ता नाम या ईमेल पता टाइप किया गया है - डेटा का कुछ टुकड़ा जिसे यह देखने के लिए जांचना होगा कि क्या यह पहले से मौजूद है ताकि यह सुनिश्चित हो सके कि ऐसी चीजें अद्वितीय रहें।

तो, एक उदाहरण के रूप में, मान लें कि उपयोगकर्ता कंपनी के नाम के रूप में "मेरी मौजूदा कंपनी" में प्रवेश करता है और यह कंपनी पहले से मौजूद है। वे डेटा दर्ज करते हैं और Submit दबाते हैं। इस बटन की क्लिक घटना को इस तरह से तार-तार कर दिया जाएगा कि एपीआई एंडपॉइंट के लिए एक एसिंक अनुरोध को कॉल किया जाएगा - कुछ इस तरह: companyFetch('acct_mgmt/companies/name/My%20Existing%20Company')

फिर घटक में एक useEffect निर्माण होगा जो एंडपॉइंट से प्रतिक्रिया के वापस आने की प्रतीक्षा करेगा। ऐसा कोड इस तरह दिख सकता है:

  useEffect(() => {
    if (!companyName.isLoading && acctMgmtContext.companyName.length > 0) {
      if (fleetName.status === 200) {  
        const errorMessage = 'This company name already exists in the system.';
        updateValidationErrors(name, {type: 'fetch', message: errorMessage});
      } else {
        clearValidationError(name);
        changeWizardIndex('+1');
      }
    }
  }, [companyName.isLoading, companyName.isError, companyName.data]);

इस कोड में ठीक ऊपर, कंपनी का नाम मौजूद होने पर एक त्रुटि दिखाई जाती है। यदि यह अभी तक मौजूद नहीं है तो यह घटक जिस विज़ार्ड में रहता है वह आगे बढ़ जाएगा। यहां मुख्य बात यह है कि प्रतिक्रिया को संभालने के लिए सभी तर्क useEffect में निहित हैं।

यह सब ठीक काम करता है जब तक कि उपयोगकर्ता एक ही कंपनी के नाम को लगातार दो बार दर्ज नहीं करता है। इस विशेष मामले में, url निर्भरता companyFetch के उदाहरण में useFetch नहीं बदलती है और इस प्रकार एपीआई एंडपॉइंट पर कोई नया अनुरोध नहीं भेजा गया है।

मैं इसे हल करने के कई तरीकों के बारे में सोच सकता हूं लेकिन वे सभी हैक की तरह लगते हैं। मैं सोच रहा हूं कि दूसरों को पहले इस समस्या का सामना करना पड़ा होगा और मैं उत्सुक हूं कि उन्होंने इसे कैसे हल किया।

0
robertwerner_sf 25 सितंबर 2019, 23:00

2 जवाब

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

यदि आप ऐसा करना चाहते हैं, तो अपने useFetch में useCallback का उपयोग करें ताकि आप एक अंतहीन लूप न बनाएं:

const triggerFetch = useCallback(async () => {
  console.log('Starting useCallback in requests.useFetch', Date.now());
  const options = appStore.awsConfig;

  const fetchData = async () => {
    dispatch({ type: 'FETCH_INIT' });

    try {
      let response = {};
      if (url && options) {
        response = await axios.get(url, options);
      }

        dispatch({ type: 'FETCH_SUCCESS', payload: response });
    } catch (error) {
      // We won't force an error if there's no URL
      if (url !== null) {
        dispatch({ type: 'FETCH_FAILURE', payload: error.response });
      }
    }
  };

  fetchData();

}, [url, appStore.awsConfig]);

..और हुक के अंत में:

 return [state, setUrl, triggerFetch];

अब आप useEffect में हर मामले की जांच करने के बजाय डेटा को प्रोग्रामेटिक रूप से रीफ़ेच करने के लिए अपने उपभोग करने वाले घटक में कहीं भी triggerRefetch() का उपयोग कर सकते हैं।

यहाँ एक पूरा उदाहरण है:

कोडसैंडबॉक्स: ट्रिगर के साथ उपयोग करें

1
Bennett Dams 29 सितंबर 2019, 14:36

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

const [requestIndex, incRequest] = useState(0);
...
const [data, updateURl] = useFetch(`${url}&random=${requestIndex}`);
const onSearchClick = useCallback(() => {
  incRequest();
}, []);
0
skyboyer 29 सितंबर 2019, 15:04
मुझे पॉल कोवान का यह लेख और उसके बाद की टिप्पणियाँ, बहुत ज्ञानवर्धक लगी: ब्लॉग .logrocket.com/frustrations-with-react-hooks अगर आप 17 सितंबर को दोपहर 3:15 बजे Ciprian @3:15 बजे तक टिप्पणी तक स्क्रॉल करते हैं, तो आप ऊपर दिए गए मेरे जैसा दृष्टिकोण देखेंगे, लेकिन एक यह चलने लगा। उस ने कहा, Karen Grigoryan की पहली टिप्पणी सबसे दिलचस्प थी। इसमें, वह बताते हैं कि एक ईवेंट हैंडलर से सीधे async फ़ंक्शन को कॉल करना, रिएक्ट में एक अच्छा तरीका नहीं है। मैंने एक राज्य चर सेट करके इस दृष्टिकोण का परीक्षण किया है, जो स्वयं, उपयोग प्रभाव को ट्रिगर करता है। यह सबसे अच्छा लगता है!
 – 
robertwerner_sf
29 सितंबर 2019, 21:43