ट्रेस क्यों एक प्रतिक्रिया घटक फिर से प्रतिपादन है


156

क्या डीबग करने के लिए एक व्यवस्थित दृष्टिकोण है जो रिएक्ट में एक घटक को फिर से प्रस्तुत करने का कारण बन रहा है? मैंने यह देखने के लिए एक सरल कंसोल.लॉग () लगाया कि यह कितनी बार प्रस्तुत होता है, लेकिन मुझे यह पता लगाने में परेशानी हो रही है कि घटक मेरे मामले में कई बार (4 बार) रेंडर करने के लिए क्या कारण है। क्या कोई ऐसा उपकरण मौजूद है जो एक समयरेखा और / या सभी घटकों के पेड़ रेंडरर्स और ऑर्डर दिखाता है?


हो सकता है कि आप shouldComponentUpdateऑटोमैटिक कंपोनेंट अपडेट को डिसेबल कर सकें और फिर वहां से अपना ट्रेस शुरू कर सकें । अधिक जानकारी यहां पाई जा सकती है: facebook.github.io/react/docs/optimizing-performance.html
रेजा सदर

@jpdelatorre का उत्तर सही है। सामान्य तौर पर, रिएक्ट की एक ताकत यह है कि आप आसानी से कोड को देखकर चेन के डेटा प्रवाह का पता लगा सकते हैं। प्रतिक्रिया DevTools विस्तार उस के साथ कर सकते हैं मदद करते हैं। इसके अलावा, मेरे पास अपने Redux addons कैटलॉग के भाग के रूप में रिएक्ट कंपोनेंट को पुन: प्रस्तुत करने / देखने के लिए उपयोगी उपकरणों की एक सूची है , और [रिएक्ट परफॉर्मेंस मॉनिटरिंग] पर कई लेख (चित्र
मार्कर

जवाबों:


253

यदि आप किसी बाहरी निर्भरता के बिना एक छोटा स्निपेट चाहते हैं तो मुझे यह उपयोगी लगता है

componentDidUpdate(prevProps, prevState) {
  Object.entries(this.props).forEach(([key, val]) =>
    prevProps[key] !== val && console.log(`Prop '${key}' changed`)
  );
  if (this.state) {
    Object.entries(this.state).forEach(([key, val]) =>
      prevState[key] !== val && console.log(`State '${key}' changed`)
    );
  }
}

यहाँ एक छोटा सा हुक है जिसका उपयोग मैं फ़ंक्शन घटकों को अपडेट ट्रेस करने के लिए करता हूं

function useTraceUpdate(props) {
  const prev = useRef(props);
  useEffect(() => {
    const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
      if (prev.current[k] !== v) {
        ps[k] = [prev.current[k], v];
      }
      return ps;
    }, {});
    if (Object.keys(changedProps).length > 0) {
      console.log('Changed props:', changedProps);
    }
    prev.current = props;
  });
}

// Usage
function MyComponent(props) {
  useTraceUpdate(props);
  return <div>{props.children}</div>;
}

5
@ yarden.refaeli मुझे एक ब्लॉक होने का कोई कारण नहीं दिखता। संक्षिप्त और संक्षिप्त।
आइजैक

इसके साथ ही, यदि आप पाते हैं कि राज्य का एक टुकड़ा अपडेट किया जा रहा है और यह स्पष्ट नहीं है कि कहाँ या क्यों, आप setStateविधि (एक कक्षा घटक में) को ओवरराइड कर सकते हैं setState(...args) { super.setState(...args) }और फिर अपने डीबगर में एक ब्रेकपॉइंट सेट कर सकते हैं जिसे आप तब सक्षम कर पाएंगे राज्य की स्थापना समारोह में वापस ट्रेस करने के लिए।
redbmk

मैं हुक फ़ंक्शन का उपयोग कैसे करूं? जैसा कि useTraceUpdateमैंने इसे लिखने के बाद परिभाषित किया है कि वास्तव में मैं कहां कॉल करने वाला हूं?
डेमन जूल

एक फंक्शन कंपोनेंट में, आप इसे इस तरह से इस्तेमाल कर सकते हैं function MyComponent(props) { useTraceUpdate(props); }और जब भी प्रॉपर बदलाव होगा यह लॉग करेगा
याकूब रस

1
@DawsonB आप शायद उस घटक में कोई राज्य नहीं है, इसलिए this.stateअपरिभाषित है।
जैकब रस्क

67

यहां कुछ उदाहरण दिए गए हैं कि एक रिएक्ट घटक फिर से प्रस्तुत करेगा।

  • मूल घटक रेंडरर
  • this.setState()घटक के भीतर बुला रहा है । यह निम्न घटक जीवन चक्र के तरीकों को गति प्रदान करेगा shouldComponentUpdate> componentWillUpdate> render>componentDidUpdate
  • घटक में परिवर्तन props। हो जाएगा ताकि ट्रिगर componentWillReceiveProps> shouldComponentUpdate> componentWillUpdate> render> componentDidUpdate( connectकी विधि react-reduxट्रिगर इस जब वहाँ Redux दुकान में लागू परिवर्तन कर रहे हैं)
  • कॉलिंग this.forceUpdateजो के समान हैthis.setState

आप अपने कंपोनेंट के रेंडर को अपने अंदर एक चेक को लागू करके shouldComponentUpdateऔर falseयदि आवश्यक नहीं है, तो वापस कर सकते हैं।

एक अन्य तरीका उपयोग React.PureComponent या स्टेटलेस घटकों का है। शुद्ध और स्टेटलेस कंपोनेंट केवल फिर से प्रस्तुत करते हैं जब इसमें बदलाव होते हैं।


6
नाइटपिक: "स्टेटलेस" का मतलब बस किसी भी घटक से है जो राज्य का उपयोग नहीं करता है, चाहे वह वर्ग सिंटैक्स या कार्यात्मक सिंटैक्स के साथ परिभाषित किया गया हो। इसके अलावा, कार्यात्मक घटक हमेशा पुन: प्रस्तुत करते हैं। आपको परिवर्तन पर केवल पुन: प्रतिपादन लागू करने के लिए shouldComponentUpdateया तो उपयोग करने या विस्तार करने की आवश्यकता है React.PureComponent
21

1
आप स्टेटलेस / फंक्शनल कंपोनेंट के बारे में सही हैं जो हमेशा रेंडर करते हैं। मेरे उत्तर को अपडेट करेंगे।
jpdelatorre

क्या आप स्पष्ट कर सकते हैं कि कब और क्यों कार्यात्मक घटक हमेशा पुन: प्रतिपादन करते हैं? मैं अपने ऐप में काफी कार्यात्मक घटकों का उपयोग करता हूं।
जसन

तो भले ही आप अपने घटक बनाने के कार्यात्मक तरीके का उपयोग करें जैसे const MyComponent = (props) => <h1>Hello {props.name}</h1>;(यह एक स्टेटलेस घटक है)। जब भी मूल घटक पुन: रेंडर करता है, तो यह फिर से रेंडर हो जाएगा।
jpdelatorre

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

10

@ jpdelatorre का जवाब सामान्य कारणों को उजागर करने में बहुत अच्छा है कि एक रिएक्ट घटक फिर से क्यों प्रस्तुत कर सकता है।

मैं बस एक उदाहरण में थोड़ा गहरा गोता लगाना चाहता था: जब प्रॉप्स बदलते हैं । समस्या निवारण जो पुन: प्रस्तुत करने के लिए एक रिएक्ट घटक का कारण बन रहा है, वह एक सामान्य समस्या है, और मेरे अनुभव में इस मुद्दे को ट्रैक करने के लिए कई बार यह निर्धारित करना शामिल है कि कौन सा प्रॉपर बदल रहा है

जब भी वे नए प्रॉप्स प्राप्त करते हैं तो रिएक्ट घटकों को फिर से प्रस्तुत करें। वे नए प्रॉप्स प्राप्त कर सकते हैं जैसे:

<MyComponent prop1={currentPosition} prop2={myVariable} />

या यदि MyComponentवह किसी रिड्यूस स्टोर से जुड़ा हो:

function mapStateToProps (state) {
  return {
    prop3: state.data.get('savedName'),
    prop4: state.data.get('userCount')
  }
}

किसी भी समय का मूल्य prop1, prop2, prop3, या prop4परिवर्तन MyComponentफिर से प्रस्तुत करना होगा। 4 प्रॉप्स के साथ यह ट्रैक करना बहुत मुश्किल नहीं है console.log(this.props)कि प्रॉप को renderब्लॉक की शुरुआत में लगाकर किस तरह से बदल रहे हैं । हालांकि अधिक जटिल घटकों और अधिक से अधिक सहारा के साथ यह विधि अस्थिर है।

यहां एक उपयोगी दृष्टिकोण ( सुविधा के लिए लॉश का उपयोग करके ) यह निर्धारित करने के लिए है कि कौन से प्रोपर परिवर्तन एक घटक को फिर से प्रस्तुत करने का कारण बन रहे हैं:

componentWillReceiveProps (nextProps) {
  const changedProps = _.reduce(this.props, function (result, value, key) {
    return _.isEqual(value, nextProps[key])
      ? result
      : result.concat(key)
  }, [])
  console.log('changedProps: ', changedProps)
}

इस स्निपेट को अपने कंपोनेंट में जोड़ने से अपराधी को संदिग्ध री-रेंडर करने में मदद मिल सकती है, और कई बार यह अनावश्यक डेटा को घटकों में पाइप किए जाने पर शेड को कम करने में मदद करता है।


3
यह अब कहा जाता है UNSAFE_componentWillReceiveProps(nextProps)और यह पदावनत है। "यह जीवनचक्र पहले नामित किया गया था componentWillReceiveProps। यह नाम संस्करण 17 तक काम करना जारी रखेगा।" से प्रलेखन प्रतिक्रिया
एमिल बर्जरॉन

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

5

अजीब बात है कि किसी ने भी इसका जवाब नहीं दिया है, लेकिन मुझे यह बहुत उपयोगी लगता है, खासकर जब से प्रॉप्स परिवर्तन लगभग हमेशा गहरे घोंसले के शिकार होते हैं।

हुक प्रशंसक

import deep_diff from "deep-diff";
const withPropsChecker = WrappedComponent => {
  return props => {
    const prevProps = useRef(props);
    useEffect(() => {
      const diff = deep_diff.diff(prevProps.current, props);
      if (diff) {
        console.log(diff);
      }
      prevProps.current = props;
    });
    return <WrappedComponent {...props} />;
  };
};

"ओल्ड" -स्कूल के प्रशंसक

import deep_diff from "deep-diff";
componentDidUpdate(prevProps, prevState) {
      const diff = deep_diff.diff(prevProps, this.props);
      if (diff) {
        console.log(diff);
      }
}

PS मैं अभी भी HOC (उच्चतर ऑर्डर कंपोनेंट) का उपयोग करना पसंद करता हूं क्योंकि कभी-कभी आपने अपने प्रॉप्स को सबसे ऊपर नष्ट कर दिया है और जैकब का समाधान ठीक नहीं है

अस्वीकरण: पैकेज मालिक के साथ कोई संबद्धता नहीं। बस के आसपास दसियों बार क्लिक करने के लिए गहरी नेस्टेड वस्तुओं में अंतर को देखने की कोशिश करना एक दर्द है।


4

अब npm पर इसके लिए एक हुक उपलब्ध है:

https://www.npmjs.com/package/use-trace-update

(प्रकटीकरण, मैंने इसे प्रकाशित किया) अपडेट: जैकब रस्क के कोड के आधार पर विकसित


14
यह वास्तव में वही कोड है जो जैकब ने पोस्ट किया था। वहाँ उसे श्रेय दिया जा सकता था।
क्रिश्चियन आइविसेविच

2

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

मैं इस भाग को घटक की फ़ाइल में चिपकाता हूँ:

const keys = {};
const checkDep = (map, key, ref, extra) => {
  if (keys[key] === undefined) {
    keys[key] = {key: key};
    return;
  }
  const stored = map.current.get(keys[key]);

  if (stored === undefined) {
    map.current.set(keys[key], ref);
  } else if (ref !== stored) {
    console.log(
      'Ref ' + keys[key].key + ' changed',
      extra ?? '',
      JSON.stringify({stored}).substring(0, 45),
      JSON.stringify({now: ref}).substring(0, 45),
    );
    map.current.set(keys[key], ref);
  }
};

विधि की शुरुआत में मैं एक WeakMap संदर्भ रखता हूं:

const refs = useRef(new WeakMap());

फिर प्रत्येक "संदिग्ध" कॉल (प्रॉप्स, हुक) के बाद मैं लिखता हूं:

const example = useExampleHook();
checkDep(refs, 'example ', example);

1

उपरोक्त उत्तर बहुत मददगार हैं, बस अगर किसी को रेंडरर के कारण का पता लगाने के लिए एक विशिष्ट विधि की तलाश है तो मुझे यह लाइब्रेरी redux-logger बहुत मददगार लगी

आप जो कर सकते हैं वह है पुस्तकालय जोड़ना और राज्य के बीच अंतर को सक्षम करना (यह डॉक्स में है) जैसे:

const logger = createLogger({
    diff: true,
});

और स्टोर में मिडलवेयर को जोड़ें।

फिर console.log()उस घटक के रेंडर फ़ंक्शन में डालें जिसे आप परीक्षण करना चाहते हैं।

फिर आप अपना ऐप चला सकते हैं और कंसोल लॉग के लिए जांच कर सकते हैं। कहीं भी लॉग होने से पहले यह आपको राज्य के बीच अंतर दिखाएगा (nextProps and this.props)और आप तय कर सकते हैं कि क्या रेंडर वास्तव में आवश्यक हैयहाँ छवि विवरण दर्ज करें

यह ऊपर की छवि के साथ साथ समान कुंजी के समान होगा।

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