setState () ComponentsDidUpdate के अंदर ()


131

मैं एक स्क्रिप्ट लिख रहा हूं जो ड्रॉपडाउन की ऊंचाई या स्क्रीन पर इनपुट की स्थिति के आधार पर ड्रॉपडाउन से ऊपर या ऊपर ले जाता है। इसके अलावा, मैं अपनी दिशा के अनुसार मॉडिफायर को ड्रॉपडाउन पर सेट करना चाहता हूं। लेकिन एक अनंत लूप बनाता है (जो स्पष्ट है) के setStateअंदर उपयोग करनाcomponentDidUpdate

मैंने getDOMNodeसीधे ड्रॉपडाउन के लिए क्लासनाम का उपयोग करने और स्थापित करने में एक समाधान पाया है , लेकिन मुझे लगता है कि रिएक्ट टूल का उपयोग करके एक बेहतर समाधान होना चाहिए। कोई भी मेरी मदद कर सकता हैं?

यहाँ काम कोड का एक हिस्सा है getDOMNode(कोड को सरल बनाने के लिए ia थोड़ा सा उपेक्षित स्थिति तर्क)

let SearchDropdown = React.createClass({
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        el.classList.remove('dropDown-top');
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            el.classList.add('dropDown-top');
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        return (
            <DropDown >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

और यहाँ सेटस्टेट के साथ कोड है (जो एक अनंत लूप बनाता है)

let SearchDropdown = React.createClass({
    getInitialState() {
        return {
            top: false
        };
    },
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        if (this.state.top) {
           this.setState({top: false});
        }
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            if (!this.state.top) {
              this.setState({top: true});
           }
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        let class = cx({'dropDown-top' : this.state.top});
        return (
            <DropDown className={class} >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

9
मुझे लगता है कि चाल है कि यहाँ है setStateजाएगा हमेशा एक फिर से प्रस्तुत करना ट्रिगर। कई बार चेक करने state.topऔर कॉल करने के बजाय setState, केवल उसी चीज़ को ट्रैक करें जिसे आप state.topस्थानीय वैरिएबल में रखना चाहते हैं , फिर componentDidUpdateकॉल के अंत में setStateकेवल एक बार अगर आपका स्थानीय वैरिएबल मेल नहीं खाता है state.top। जैसा कि यह अभी खड़ा है, आप state.topपहले री-रेंडर के तुरंत बाद रीसेट कर देते हैं, जो आपको अनंत लूप में डाल देता है।
रैंडी मॉरिस

2
इस फिडलcomponentDidUpdate में दो अलग-अलग कार्यान्वयन देखें ।
रैंडी मॉरिस

लानत है! स्थानीय चर पूरे समस्या को हल करता है, मैं इसे mysef द्वारा कैसे समझ नहीं पाया था! धन्यवाद!
कतेरीना पावेलेंको

1
मुझे लगता है कि आपको नीचे दिए गए उत्तर को स्वीकार करना चाहिए। यदि आप इसे फिर से पढ़ते हैं तो मुझे लगता है कि आप पाएंगे कि यह प्रारंभिक प्रश्न का पर्याप्त रूप से उत्तर देता है।
रैंडी मॉरिस

किसी ने भी इस शर्त को आगे बढ़ाने का सुझाव क्यों नहीं दिया componentShouldUpdate?
पैट्रिक रॉबर्ट्स

जवाबों:


116

आप setStateअंदर उपयोग कर सकते हैं componentDidUpdate। समस्या यह है कि किसी भी तरह से आप एक अनंत लूप बना रहे हैं क्योंकि कोई ब्रेक की स्थिति नहीं है।

इस तथ्य के आधार पर कि आपको घटक द्वारा प्रदान किए जाने के बाद ब्राउज़र द्वारा प्रदान किए जाने वाले मानों की आवश्यकता होती है, मुझे लगता है कि उपयोग करने के बारे में आपका दृष्टिकोण componentDidUpdateसही है, इसे बस उस स्थिति से बेहतर ढंग से निपटने की आवश्यकता है जो इसे ट्रिगर करती है setState


4
'ब्रेक कंडीशन' से आपका क्या मतलब है? जाँच करें कि क्या राज्य पहले से ही सेट है और इसे रीसेट नहीं कर रहा है?
कतेरीना पावेलेंको

मैं इससे सहमत हूं, मेरी एकमात्र अतिरिक्त टिप्पणी यह ​​होगी कि कक्षाओं को जोड़ना / हटाना संभवतः अनावश्यक है componentDidUpdateऔर renderइसके बजाय आवश्यकतानुसार जोड़ा जा सकता है ।
रैंडी मॉरिस

लेकिन क्लास जोड़ना / हटाना ड्रॉपडाउन स्थिति पर निर्भर करता है जो कि घटकडिपडेट में जाँच की जाती है, आप इसे दो बार जांचना चाहते हैं? और जैसा कि मुझे समझ में आया है, ComponentsDidUpdate को AFTER रेंडर () कहा जाता है, इसलिए रेंडर में क्लास जोड़ना / हटाना बेकार है ()
कतेरीना पावेलेंको

मैंने अपना कोड सेटस्टेट के साथ जोड़ा है, क्या आप इसे जांच सकते हैं और मुझे मेरी गलती की ओर इशारा कर सकते हैं? या मुझे कुछ उदाहरण दिखाओ जो लूप का कारण नहीं
बनेंगे

2
ComponentsDidUpdate (prevProps, prevState) {अगर (prevState.x! == this.state.x) {// कुछ करो}}
अशोक आर

68

componentDidUpdateहस्ताक्षर है void::componentDidUpdate(previousProps, previousState)। इससे आप यह जांचने में सक्षम होंगे कि कौन सा प्रॉप्स / राज्य गंदा है और setStateतदनुसार कॉल करें ।

उदाहरण:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}

componentDidMountकोई तर्क नहीं है, और केवल तब कहा जाता है जब घटक बनाया जाता है, इसलिए वर्णित उद्देश्य के लिए उपयोग नहीं किया जा सकता है।
जूल्स

@ जूल्स थैंक्स! मैं लिखता था componentDidMount, इसलिए जब मैंने प्रसिद्ध नाम कैस्केड का उत्तर लिखा तो so अगेन, थैंक्स एंड ग्रेट कैच अप!
अब्देनौर टुमी

componentDidUpdate(prevProps, prevState) { if ( prevState.x!== this.state.x) { //Do Something } }
अशोक आर

मुझे पता है आपकी चिंता @AshokR आप आर्ग नाम को कम करते हैं। लेकिन "प्रबल" का मतलब पिछले नहीं रोका जा सकता .. hhh। .कीडिंग :)
अब्देनूर टुमी

58

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

componentDidUpdate: function() {
    if (condition) {
        this.setState({..})
    } else {
        //do something else
    }
}

यदि आप केवल प्रॉप्स को भेजकर घटक को अपडेट कर रहे हैं (यह सेटस्टैट द्वारा अपडेट नहीं किया जा रहा है, तो घटकडायपडेट के अंदर के मामले को छोड़कर), आप इसके बजाय setStateअंदर कॉल कर सकते componentWillReceivePropsहैं componentDidUpdate


2
पुराने प्रश्न लेकिन कंपोनेंटलीरिविवप्रॉप्स को पदावनत कर दिया गया है और कंपोनेंटविलक्रैपीप्रॉप्स का उपयोग किया जाना चाहिए। आप इस विधि के अंदर सेट नहीं कर सकते।
ब्रुक डुबोइस

आपका मतलब है getDerivedStateFromProps
adi518

5

यह उदाहरण आपको प्रतिक्रिया जीवन चक्र हुक को समझने में मदद करेगा ।

आप विधि setStateमें कर सकते हैं getDerivedStateFromPropsयानी staticप्रॉपर बदलाव के बाद विधि को ट्रिगर कर सकते हैं componentDidUpdate

इसमें componentDidUpdateआपको 3 परम मिलेंगे जो कि से लौटते हैं getSnapshotBeforeUpdate

आप इस कोडैंडबॉक्स लिंक को देख सकते हैं

// Child component
class Child extends React.Component {
  // First thing called when component loaded
  constructor(props) {
    console.log("constructor");
    super(props);
    this.state = {
      value: this.props.value,
      color: "green"
    };
  }

  // static method
  // dont have access of 'this'
  // return object will update the state
  static getDerivedStateFromProps(props, state) {
    console.log("getDerivedStateFromProps");
    return {
      value: props.value,
      color: props.value % 2 === 0 ? "green" : "red"
    };
  }

  // skip render if return false
  shouldComponentUpdate(nextProps, nextState) {
    console.log("shouldComponentUpdate");
    // return nextState.color !== this.state.color;
    return true;
  }

  // In between before real DOM updates (pre-commit)
  // has access of 'this'
  // return object will be captured in componentDidUpdate
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log("getSnapshotBeforeUpdate");
    return { oldValue: prevState.value };
  }

  // Calls after component updated
  // has access of previous state and props with snapshot
  // Can call methods here
  // setState inside this will cause infinite loop
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate: ", prevProps, prevState, snapshot);
  }

  static getDerivedStateFromError(error) {
    console.log("getDerivedStateFromError");
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.log("componentDidCatch: ", error, info);
  }

  // After component mount
  // Good place to start AJAX call and initial state
  componentDidMount() {
    console.log("componentDidMount");
    this.makeAjaxCall();
  }

  makeAjaxCall() {
    console.log("makeAjaxCall");
  }

  onClick() {
    console.log("state: ", this.state);
  }

  render() {
    return (
      <div style={{ border: "1px solid red", padding: "0px 10px 10px 10px" }}>
        <p style={{ color: this.state.color }}>Color: {this.state.color}</p>
        <button onClick={() => this.onClick()}>{this.props.value}</button>
      </div>
    );
  }
}

// Parent component
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: 1 };

    this.tick = () => {
      this.setState({
        date: new Date(),
        value: this.state.value + 1
      });
    };
  }

  componentDidMount() {
    setTimeout(this.tick, 2000);
  }

  render() {
    return (
      <div style={{ border: "1px solid blue", padding: "0px 10px 10px 10px" }}>
        <p>Parent</p>
        <Child value={this.state.value} />
      </div>
    );
  }
}

function App() {
  return (
    <React.Fragment>
      <Parent />
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


2

मैं कहूंगा कि आपको यह जांचने की आवश्यकता है कि क्या राज्य के पास पहले से वही मूल्य है जिसे आप सेट करने का प्रयास कर रहे हैं। यदि यह समान है, तो समान मूल्य के लिए फिर से राज्य निर्धारित करने का कोई मतलब नहीं है।

अपना राज्य इस तरह सेट करना सुनिश्चित करें:

let top = newValue /*true or false*/
if(top !== this.state.top){
    this.setState({top});
}

-1

मुझे एक समान समस्या थी जहां मुझे टूलटिप को केंद्र में रखना होगा। React setState in ComponentsDidUpdate ने मुझे अनंत पाश में डाल दिया, मैंने कोशिश की कि यह काम करे। लेकिन मैंने पाया कि रिफ कॉलबैक में उपयोग ने मुझे सरल और साफ समाधान दिया है, यदि आप रिफ कॉलबैक के लिए इनलाइन फ़ंक्शन का उपयोग करते हैं तो आपको हर घटक अपडेट के लिए अशक्त समस्या का सामना करना पड़ेगा। इसलिए Ref कॉलबैक में फ़ंक्शन संदर्भ का उपयोग करें और राज्य को वहां सेट करें, जो फिर से रेंडर करेगा

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