componentDidUpdate () içinde setState ()


132

Açılır menünün yüksekliğine ve girişin ekrandaki konumuna bağlı olarak açılır menüyü girişin altına veya üstüne taşıyan bir komut dosyası yazıyorum. Ayrıca değiştiriciyi yönüne göre açılır menüye ayarlamak istiyorum. Ama kullanaraksetState içinicomponentDidUpdate sonsuz bir döngü yaratır (ki bu açıktır)

Kullanarak bir çözüm buldum getDOMNodeDoğrudan açılır listeye sınıf adını ve ayarlama konusunda , ancak React araçlarını kullanarak daha iyi bir çözüm olması gerektiğini düşünüyorum. Birisi bana yardım edebilir mi?

İşte kodun getDOMNode(kodu basitleştirmek için biraz ihmal edilmiş bir konumlandırma mantığı) ile çalışma kodunun bir parçası.

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>
        );
    }
});

ve işte setstate ile kod (sonsuz bir döngü oluşturan)

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
Bence buradaki hile setState, her zaman bir yeniden oluşturmayı tetikleyecek. Birden çok kez kontrol etmek state.topve aramak yerine , yerel bir değişkende setStatene olmasını istediğinizi state.top, ardından yalnızca yerel değişkeniniz eşleşmiyorsa componentDidUpdatearama sonunda bir kez setStateizleyin state.top. Şu anda olduğu gibi state.top, ilk yeniden oluşturmadan hemen sonra sıfırlarsınız , bu da sizi sonsuz döngüye sokar.
Randy Morris

2
İki farklı uygulamaları görün componentDidUpdatede bu keman .
Randy Morris

lanet olsun! yerel değişken tüm sorunu çözüyor, bunu mysef ile nasıl çözemedim! Teşekkür ederim!
Katerina Pavlenko

1
Aşağıdaki cevabı kabul etmeniz gerektiğini düşünüyorum. Tekrar okursanız, ilk soruyu yeterince cevapladığını göreceksiniz.
Randy Morris,

Neden kimse durumu değiştirmeyi önermedi componentShouldUpdate?
Patrick Roberts

Yanıtlar:


116

setStateİçeride kullanabilirsiniz componentDidUpdate. Sorun şu ki, bir şekilde sonsuz bir döngü yaratıyorsunuz çünkü kırılma koşulu yok.

Bileşen oluşturulduktan sonra tarayıcı tarafından sağlanan değerlere ihtiyacınız olduğu gerçeğine dayanarak, kullanma konusundaki yaklaşımınızın componentDidUpdatedoğru olduğunu düşünüyorum , sadece setState.


4
Mola durumu ile ne demek istiyorsun? durumun önceden ayarlanıp ayarlanmadığını kontrol ediyor ve sıfırlamıyor mu?
Katerina Pavlenko

Buna katılıyorum, tek ek yorumum, sınıfların eklenmesi / kaldırılmasının muhtemelen gereksiz olduğu componentDidUpdateve renderbunun yerine gerektiği gibi eklenebileceğidir .
Randy Morris

ancak sınıf ekleme / çıkarma, componentDidUpdate'te iade edilen açılır konuma bağlıdır, iki kez kontrol etmenizi önerirsiniz? Ve anladığım kadarıyla, componentDidUpdate, render () 'dan SONRA, bu yüzden render ()' a sınıf eklemek / kaldırmak gereksizdir
Katerina Pavlenko,

kodumu setstate ile ekledim, kontrol edip hatamı gösterebilir misiniz? veya bana döngüye neden olmayacak bir örnek gösterin
Katerina Pavlenko

2
componentDidUpdate (prevProps, prevState) {if (prevState.x! == this.state.x) {// Bir Şey Yapın}}
Ashok R

70

componentDidUpdateİmzadır void::componentDidUpdate(previousProps, previousState). Bununla hangi sahne / durumun kirli olduğunu test edebilir vesetState göre .

Misal:

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

componentDidMountherhangi bir argümana sahip değildir ve yalnızca bileşen oluşturulduğunda çağrılır, bu nedenle açıklanan amaç için kullanılamaz.
Jules

@Jules Teşekkürler! Eskiden componentDidMountyazardım, bu yüzden cevabı yazdığımda ünlü isim basamaklandı Tekrar, Teşekkürler ve Harika yakalayın!
Abdennour TOUMI

componentDidUpdate(prevProps, prevState) { if ( prevState.x!== this.state.x) { //Do Something } }
Ashok R

Endişenizi biliyorum @AshokR. Arg adını azaltırsınız. ancak "önceki", önceki değil önlemek anlamına gelebilir .. hhh. .kidding :)
Abdennour TOUMI

58

Eğer setStateiçinde kullanırsanız componentDidUpdate, bileşeni günceller ve componentDidUpdatedaha sonra setStatetekrar çağıran ve sonsuz döngü ile sonuçlanan bir çağrı ile sonuçlanır. Koşullu olarak aramalı setStateve aramayı ihlal eden koşulun sonunda ortaya çıkmasını sağlamalısınız, örneğin:

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

Bileşeni sadece props göndererek güncelliyorsanız (componentDidUpdate'in içindeki durum dışında setState tarafından güncellenmiyorsa), yerine setStateiçini çağırabilirsiniz .componentWillReceivePropscomponentDidUpdate


2
eski soru ama componentWillReceiveProps kullanımdan kaldırıldı ve componentWillRecieveProps kullanılmalıdır. Bu yöntem içindeState ayarlayamazsınız.
Brooks DuBois

Yani getDerivedStateFromProps.
adi518

5

Bu örnek, React Yaşam Döngüsü Kancalarını anlamanıza yardımcı olacaktır .

Şunları yapabilirsiniz setStateyılında getDerivedStateFromPropsyani yöntemde staticve sahne içinde değiştirdikten sonra yöntemini tetikler componentDidUpdate.

İçinde dönen 3. param componentDidUpdatealacaksınız .getSnapshotBeforeUpdate

Bu kod ve kutu bağlantısını kontrol edebilirsiniz

// 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

Durumun zaten ayarlamaya çalıştığınız değere sahip olup olmadığını kontrol etmeniz gerektiğini söyleyebilirim. Aynıysa, aynı değer için durumu yeniden ayarlamanın bir anlamı yoktur.

Durumunuzu şu şekilde ayarladığınızdan emin olun:

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

-1

Araç ipucunu ortalamam gereken benzer bir sorun yaşadım. ComponentDidUpdate'teki React setState beni sonsuz döngüye soktu, çalıştığı koşulu denedim. Ancak ref geri aramada kullanmanın bana daha basit ve temiz bir çözüm sağladığını buldum, ref geri arama için satır içi işlevi kullanırsanız, her bileşen güncellemesinde boş sorunla karşılaşacaksınız. Bu nedenle ref geri çağırmada işlev referansını kullanın ve durumu orada ayarlayın, böylece yeniden oluşturmayı başlatır

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.