Reaksiyon Formu'ndaki sahne değişikliği durumunun güncellenmesi


184

Bir React formuyla ilgili sorun yaşıyorum ve durumu düzgün bir şekilde yönetiyorum. Bir formda (modal) bir zaman girdi alanı var. Başlangıç ​​değeri bir durum değişkeni olarak ayarlanır getInitialStateve bir üst bileşenden iletilir. Bu kendi içinde iyi çalışıyor.

Üst bileşen aracılığıyla varsayılan başlangıç_zamanı değerini güncellemek istediğimde sorun geliyor. Güncellemenin kendisi üst bileşende aracılığıyla gerçekleşir setState start_time: new_time. Ancak formumda, varsayılan başlangıç_zamanı değeri hiçbir zaman değişmez, çünkü yalnızca bir kez tanımlanır getInitialState.

componentWillUpdateDurumda bir değişikliği zorlamak için kullanmaya çalıştım setState start_time: next_props.start_time, bu gerçekten işe yaradı, ama bana Uncaught RangeError: Maximum call stack size exceededhata verdi .

Benim sorum şu, bu durumda durumu güncellemenin doğru yolu nedir? Bunu bir şekilde yanlış mı düşünüyorum?

Mevcut Kod:

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time”)

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange

Yanıtlar:


287

tepki 16'dan beri componentWillReceiveProps sınırlandırılmıştır: bunun yerine getDerivedStateFromProps kullanın

Doğru anlarsam start_time, ModalBodybileşeni kendi durumuna atayan bir üst bileşene sahip misiniz ? Ve o zamanı bir alt bileşenden değil, üst öğeden güncellemek istiyorsunuz.

React'in bu senaryo ile başa çıkma konusunda bazı ipuçları var. (Not, bu web'den kaldırılmış eski bir makaledir. İşte bileşen sahne hakkındaki mevcut dokümana bir bağlantı ).

Durum oluşturmak için sahne kullanımı getInitialStategenellikle "gerçeğin kaynağı" nın, yani gerçek verilerin olduğu yerde çoğaltılmasına yol açar. Bunun nedeni isegetInitialState , yalnızca bileşen ilk oluşturulduğunda çağrılmasıdır.

Mümkün olduğunda, daha sonra senkronizasyondan çıkmamalarını ve bakım sorunlarına neden olmalarını sağlamak için değerleri anında hesaplayın.

Temel olarak, bir ebeveyne propsbir çocuğa atadığınızda staterender yöntemi her zaman prop güncellemesinde çağrılmaz. componentWillReceivePropsYöntemi kullanarak manuel olarak çağırmanız gerekir .

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

84
React 16
ahbap

7
@dude Henüz kullanımdan kaldırılmadı, bahsettiğiniz şey gelecekteki referansınız için sadece bir adım. Ben alıntı[..]going to be deprecated in the future
paddotk

7
@poepje Henüz onaylanmamış olabilir, ancak mevcut standart tarafından güvensiz olarak görülüyor ve muhtemelen kaçınılmalıdır
unflores

12
Peki, componentWillReceiveProps kullanımdan kaldırıldıktan sonra bunu yapmanın yeni yolu ne olmalıdır?
Boris D. Teoharov

5
@Boris Şimdi tepki ekibi temel olarak doldurulmanızı söylüyor. Size getDerivedStateFromProps adlı yeni bir yöntem sunar. Yakalama, bunun statik bir yöntem olmasıdır. Yani durumu güncellemek için eşzamansız bir şey yapamazsınız (çünkü yeni durumu hemen iade etmeniz gerekir), sınıf yöntemlerine veya alanlarına da erişemezsiniz. Notu da kullanabilirsiniz, ancak bu her kullanım durumuna uymaz. Bir kez daha, tepki ekibi bir şeyler yapma yollarını zorlamak istiyor. Son derece aptalca ve etkisiz bir tasarım kararı.
ig-dev

76

Görünüşe göre işler değişiyor .... getDerivedStateFromProps () artık tercih edilen işlevdir.

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(danburzo @ github tarafından yukarıdaki kod)


7
Bilginize, nulleğer bir şey hemen sonra değişmezse , geri dönmeniz gerekiyorsa , gitmelisinizreturn null
Ilgıt Yıldırım

@ IlgıtYıldırım - 4 kişi yorumunuzu iptal ettiğinden beri kodu düzenlediniz - bu gerçekten bir fark yaratıyor mu?
ErichBSchulz

Orada farklı seçenekler üzerinde derinlere iner oldukça iyi bir kaynaktır ve ya neden kullanacağınızı getDerivedStateFromPropsveya memoization reactjs.org/blog/2018/06/07/...
unflores

2
getDerivedStateFromProps statik olmaya zorlanır. Yani durumu güncellemek için eşzamansız bir şey yapamazsınız, sınıf yöntemlerine veya alanlarına da erişemezsiniz. Bir kez daha, tepki ekibi bir şeyler yapma yollarını zorlamak istiyor. Son derece aptalca ve etkisiz bir tasarım kararı.
ig-dev

39

componentWillReceiveProps "genellikle hatalara ve tutarsızlıklara yol açar" nedeniyle kullanımdan kaldırılıyor.

Dışarıdan bir şey değişirse, alt bileşeni tamamen sıfırlamayı düşününkey .

Alt keybileşene bir destek sağlamak, keydışarıdan yapılan değişikliklerin değeri ne zaman olursa olsun , bu bileşenin yeniden oluşturulmasını sağlar. Örneğin,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Performansı hakkında:

Bu yavaş gibi görünse de , performans farkı genellikle önemsizdir. Bileşenlerin, bu alt ağaç için fark atlandığı için güncellemeler üzerinde çalışan ağır mantık varsa, bir anahtar kullanmak daha da hızlı olabilir.


1
Anahtar, sır! React 16'da yukarıda belirtildiği gibi mükemmel çalışıyor
Darren Sweeney

anahtar bir nesne değilse ve benzersiz bir
dizeniz

Anahtar nesneler için işe yarıyor, ben yaptım. Tabii ki anahtar için benzersiz bir dize vardı.
tsujp

@ user3468806 Harici referanslara sahip karmaşık bir nesne değilse, nesnenizden JSON.stringify(myObject)benzersiz bir anahtar türetmek için kullanabilirsiniz .
Roy Prins

24

Ayrıca componentDidUpdate kullanılabilir.

İşlev imza:

componentDidUpdate(prevProps, prevState, snapshot)

Bileşen güncellendiğinde DOM üzerinde çalışma fırsatı olarak bunu kullanın. Başlangıçta çağrılmaz render.

Bkz Sen Muhtemelen Devlet Türetilmiş gerekmez ikisi için Anti-Desen açıklanır Madde, componentDidUpdateve getDerivedStateFromProps. Bunu çok kullanışlı buldum.


Sonunda kullanıyorum componentDidUpdateçünkü basit ve çoğu durumda daha uygun.
KeitelDOG

15

Bunu yapmanın yeni kanca yolu eski bileşenWillReceiveProps yerine useEffect kullanmaktır:

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

fonksiyonel bir kanca tahrikli bileşende aşağıdaki gibidir:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

setState'i kullanarak durumu ayarlıyoruz, useEffect'i kullanarak belirtilen pervane değişikliklerini kontrol ediyoruz ve pervane değişikliğindeki durumu güncellemek için harekete geçiyoruz.


5

Muhtemelen Türetilmiş Duruma İhtiyacınız Yok

1. Üst öğeden bir anahtar ayarlayın

Bir anahtar değiştiğinde, React geçerli olanı güncellemek yerine yeni bir bileşen örneği oluşturur. Tuşlar genellikle dinamik listeler için kullanılır, ancak burada da yararlıdır.

2. getDerivedStateFromProps/ kullanıncomponentWillReceiveProps

Anahtar bir nedenden dolayı çalışmazsa (belki de bileşeni başlatmak çok pahalıdır)

Kullanarak getDerivedStateFromPropsdurumun herhangi bir bölümünü sıfırlayabilirsiniz, ancak şu anda biraz hatalı görünüyor (v16.7)!, Kullanım için yukarıdaki bağlantıya bakın


2

Tepki belgelerinden: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Sahne değişikliği bir Anti Desen olduğunda silme durumu

React 16'dan beri componentWillReceiveProps kullanımdan kaldırılmıştır. Reaksiyon belgelerinden, bu durumda önerilen yaklaşım

  1. Tamamen bileşen kontrollü: ParentComponentait ModalBodysahibi olacak start_timedevlet. Bu durumda benim tercih ettiğim yaklaşım bu değil çünkü modalın bu duruma sahip olması gerektiğini düşünüyorum.
  2. Tamamen kontrolsüz bileşen bir anahtarla: bu benim tercih yaklaşımım. Reaksiyon belgelerinden bir örnek: https://codesandbox.io/s/6v1znlxyxn . start_timeDevletin sizin için tamamen sahibi olacaksınız ve daha önce yaptığınız gibi ModalBodykullanabilirsiniz getInitialState. Durumu sıfırlamak için start_timeanahtarıParentComponent


0

Notu Kullan

Operasyonun durum türevi, gerçek bir türeve ihtiyaç duyulmadan sahne malzemelerinin doğrudan manipülasyonudur. Başka bir deyişle, doğrudan kullanılabilen veya dönüştürülebilen bir pervane varsa, pervane durumunu depolamaya gerek yoktur .

Durum değerinin start_timebasitçe pervane olduğu göz önüne alındığında , pervane start_time.format("HH:mm")içinde yer alan bilgiler zaten kendi içinde bileşeni güncellemek için yeterlidir.

Ancak, yalnızca pervane değişikliğinde biçimi çağırmak istediyseniz, bunu en son belgelere göre yapmanın doğru yolu Memoize aracılığıyla olacaktır: https://reactjs.org/blog/2018/06/07/you-probably-dont- gerek türetilmiş-state.html # what-yaklaşık-memoization


-1

Ref kullanımının benim için güvenli olduğunu düşünüyorum, yukarıdaki bazı yöntemlerle ilgilenmiyorum.

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}

Ben bu yanıt şifreli olduğunu düşünüyorum (kod neredeyse okunabilir ve herhangi bir açıklama / OP sorununa bağlantı olmadan) ve OP, sorun, ilk durum işlemek için nasıl mücadele etmez.
netchkin
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.