आरंभिक रेंडर पर रन रिएक्ट इफेक्ट हुक न चलाएं


106

डॉक्स के अनुसार:

componentDidUpdate()अद्यतन होने के तुरंत बाद आह्वान किया जाता है। प्रारंभिक रेंडर के लिए इस विधि को नहीं कहा जाता है।

हम नए useEffect()हुक का उपयोग अनुकरण करने के लिए कर सकते हैं componentDidUpdate(), लेकिन ऐसा लगता है कि useEffect()हर रेंडर के बाद भी दौड़ा जा रहा है, यहां तक ​​कि पहली बार भी। प्रारंभिक रेंडर पर नहीं चलने के लिए मुझे यह कैसे मिलेगा?

जैसा कि आप नीचे दिए गए उदाहरण में देख सकते हैं, componentDidUpdateFunctionप्रारंभिक रेंडर के componentDidUpdateClassदौरान मुद्रित किया गया है लेकिन प्रारंभिक रेंडर के दौरान मुद्रित नहीं किया गया था।

function ComponentDidUpdateFunction() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

class ComponentDidUpdateClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidUpdate() {
    console.log("componentDidUpdateClass");
  }

  render() {
    return (
      <div>
        <p>componentDidUpdateClass: {this.state.count} times</p>
        <button
          onClick={() => {
            this.setState({ count: this.state.count + 1 });
          }}
        >
          Click Me
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <div>
    <ComponentDidUpdateFunction />
    <ComponentDidUpdateClass />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


1
क्या मैं पूछ सकता हूं कि जब उपयोग करने वालों की संख्या के आधार पर कुछ करने का कोई मतलब नहीं है तो क्या उपयोग मामला है, जैसे कि एक स्पष्ट राज्य चर नहीं count?
अप्रैल

जवाबों:


122

हम useRefहुक का उपयोग अपने द्वारा पसंद किए जाने वाले किसी भी परिवर्तनशील मूल्य को संग्रहीत करने के लिए कर सकते हैं , इसलिए हम इसका उपयोग कर सकते हैं कि यदि यह पहली बार useEffectचल रहा है तो इसका ट्रैक कैसे रखा जाए ।

यदि हम चाहते हैं कि प्रभाव एक ही चरण में चले componentDidUpdate, तो हम useLayoutEffectइसके बजाय उपयोग कर सकते हैं ।

उदाहरण

const { useState, useRef, useLayoutEffect } = React;

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

  const firstUpdate = useRef(true);
  useLayoutEffect(() => {
    if (firstUpdate.current) {
      firstUpdate.current = false;
      return;
    }

    console.log("componentDidUpdateFunction");
  });

  return (
    <div>
      <p>componentDidUpdateFunction: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <ComponentDidUpdateFunction />,
  document.getElementById("app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


5
मैं के useRefसाथ बदलने की कोशिश की useState, लेकिन सेटर का उपयोग कर एक फिर से प्रस्तुत करना शुरू कर दिया है, जो हो रहा है जब firstUpdate.currentऐसा करने के लिए काम नहीं कर रहा है तो मुझे लगता है कि यह एकमात्र अच्छा तरीका है :)
Apr

2
क्या कोई समझा सकता है कि यदि हम DOM को म्यूट या माप नहीं रहे हैं तो लेआउट इफेक्ट का उपयोग क्यों करें?
ZenVentzi

5
@ZenVentzi इस उदाहरण में आवश्यक नहीं है, लेकिन सवाल यह था कि componentDidUpdateहुक के साथ नकल कैसे की जाए, इसलिए मैंने इसका उपयोग किया।
थोलले

1
मैंने इस उत्तर के आधार पर यहां एक कस्टम हुक बनाया । कार्यान्वयन के लिए धन्यवाद!
पैट्रिक रॉबर्ट्स

63

आप इसे कस्टम हुक में बदल सकते हैं , जैसे:

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

export default useDidMountEffect;

उपयोग उदाहरण:

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

import useDidMountEffect from '../path/to/useDidMountEffect';

const MyComponent = (props) => {    
    const [state, setState] = useState({
        key: false
    });    

    useEffect(() => {
        // you know what is this, don't you?
    }, []);

    useDidMountEffect(() => {
        // react please run me if 'key' changes, but not on initial render
    }, [state.key]);    

    return (
        <div>
             ...
        </div>
    );
}
// ...

2
यह दृष्टिकोण यह कहते हुए चेतावनी फेंकता है कि निर्भरता सूची एक सरणी शाब्दिक नहीं है।
theprogrammer

1
मैं अपनी परियोजनाओं में इस हुक का उपयोग करता हूं और मुझे कोई चेतावनी नहीं मिली, क्या आप अधिक जानकारी प्रदान कर सकते हैं?
मेहदी देघानी

1
@vsync आप एक अलग मामले के बारे में सोच रहे हैं, जहां आप एक बार प्रारंभिक रेंडर पर एक प्रभाव चलाना चाहते हैं और फिर कभी नहीं
प्रोग्रामिंग गाय

2
@vsync के नोट अनुभाग में reactjs.org/docs/... यह विशेष रूप से कहते हैं, "आप एक प्रभाव चलाने के लिए और केवल एक बार इसे स्वच्छ करें (माउंट और अनमाउंट पर) चाहते हैं, आप एक खाली सरणी पारित कर सकते हैं ([]) एक के रूप में दूसरा तर्क। " इस मैच ने मेरे लिए व्यवहार का अवलोकन किया।
प्रोग्रामिंग गाय

10

मैंने useFirstRenderफॉर्म इनपुट को फ़ोकस करने जैसे मामलों को संभालने के लिए एक साधारण हुक बनाया :

import { useRef, useEffect } from 'react';

export function useFirstRender() {
  const firstRender = useRef(true);

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

  return firstRender.current;
}

यह बाहर शुरू होता है true, फिर अंदर स्विच falseकरता है useEffect, जो केवल एक बार चलता है, और फिर कभी नहीं।

अपने घटक में, इसका उपयोग करें:

const firstRender = useFirstRender();
const phoneNumberRef = useRef(null);

useEffect(() => {
  if (firstRender || errors.phoneNumber) {
    phoneNumberRef.current.focus();
  }
}, [firstRender, errors.phoneNumber]);

अपने मामले के लिए, आप बस का उपयोग करेंगे if (!firstRender) { ...


3

@ श्रवण, तुम्हारा पास-पास अनमाउंट फंक्शन नहीं है। यहाँ एक संस्करण है जो थोड़ा अधिक पूर्ण है:

/**
 * Identical to React.useEffect, except that it never runs on mount. This is
 * the equivalent of the componentDidUpdate lifecycle function.
 *
 * @param {function:function} effect - A useEffect effect.
 * @param {array} [dependencies] - useEffect dependency list.
 */
export const useEffectExceptOnMount = (effect, dependencies) => {
  const mounted = React.useRef(false);
  React.useEffect(() => {
    if (mounted.current) {
      const unmount = effect();
      return () => unmount && unmount();
    } else {
      mounted.current = true;
    }
  }, dependencies);

  // Reset on unmount for the next mount.
  React.useEffect(() => {
    return () => mounted.current = false;
  }, []);
};


नमस्ते @ घाटब्रेन, गैर-निर्भरता सूची पास करने पर इस कस्टम हुक का उपयोग कैसे करें? एक खाली नहीं है जो कि useEffect(() => {...});
घटकडिमाउंट

1
dependenciesजब आप इसे कहते हैं तो पैरामीटर को छोड़ना जितना आसान हो सकता है @KevDing
व्हाट्रेन

0

@MehDDehagani, आपका समाधान पूरी तरह से ठीक काम करता है, इसके अलावा आपको जो करना है, वह अनमाउंट पर है, didMount.currentमान को रीसेट करें false। जब इस कस्टम हुक का उपयोग कहीं और करने की कोशिश की जाती है, तो आपको कैश मान नहीं मिलता है।

import React, { useEffect, useRef } from 'react';

const useDidMountEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        let unmount;
        if (didMount.current) unmount = func();
        else didMount.current = true;

        return () => {
            didMount.current = false;
            unmount && unmount();
        }
    }, deps);
}

export default useDidMountEffect;

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