Reactjs: Dinamik alt bileşen durumu veya ana öğeden props nasıl değiştirilir?


91

Esasen tepki olarak sekmeler yapmaya çalışıyorum, ancak bazı sorunlar var.

İşte dosya page.jsx

<RadioGroup>
    <Button title="A" />
    <Button title="B" />
</RadioGroup>

A düğmesine tıkladığınızda, RadioGroup bileşeninin B düğmesinin seçimini kaldırması gerekir .

"Seçildi" yalnızca bir durum veya özellikten bir sınıf adı anlamına gelir

İşte RadioGroup.jsx:

module.exports = React.createClass({

    onChange: function( e ) {
        // How to modify children properties here???
    },

    render: function() {
        return (<div onChange={this.onChange}>
            {this.props.children}
        </div>);
    }

});

Kaynağı Button.jsxgerçekten önemli değil, yerel DOM onChangeolayını tetikleyen normal bir HTML radyo düğmesi var

Beklenen akış:

  • "A" Düğmesine tıklayın
  • "A" düğmesi, RadioGroup'a köpüren onChange, yerel DOM etkinliğini tetikler
  • RadioGroup onChange dinleyicisi çağrılır
  • RadioGroup'un B düğmesinin seçimini kaldırması gerekir . Benim sorum bu.

İşte karşılaşmak ediyorum asıl sorun: Ben hareket edemez <Button>içine sRadioGroup bu yapısı çocuklardır şekildedir, çünkü keyfi . Yani, işaretleme olabilir

<RadioGroup>
    <Button title="A" />
    <Button title="B" />
</RadioGroup>

veya

<RadioGroup>
    <OtherThing title="A" />
    <OtherThing title="B" />
</RadioGroup>

Birkaç şey denedim.

Deneme: In RadioGrouponChange işleyicisi:

React.Children.forEach( this.props.children, function( child ) {

    // Set the selected state of each child to be if the underlying <input>
    // value matches the child's value

    child.setState({ selected: child.props.value === e.target.value });

});

Sorun:

Invalid access to component property "setState" on exports at the top
level. See react-warning-descriptors . Use a static method
instead: <exports />.type.setState(...)

Deneme: In RadioGrouponChange işleyicisi:

React.Children.forEach( this.props.children, function( child ) {

    child.props.selected = child.props.value === e.target.value;

});

Sorun: Hiçbir şey olmuyor, Buttonsınıfa bir componentWillReceivePropsyöntem versem bile


Deneme: Ebeveynin belirli bir durumunu çocuklara aktarmaya çalıştım, böylece ebeveyn durumunu güncelleyebilir ve çocukların otomatik olarak yanıt vermesini sağlayabilirim. RadioGroup'un render fonksiyonunda:

React.Children.forEach( this.props.children, function( item ) {
    this.transferPropsTo( item );
}, this);

Sorun:

Failed to make request: Error: Invariant Violation: exports: You can't call
transferPropsTo() on a component that you don't own, exports. This usually
means you are calling transferPropsTo() on a component passed in as props
or children.

Kötü çözüm 1 : React-addons.js cloneWithProps yöntemini kullanarak çocukları oluşturma zamanında RadioGrouponlara özellikleri aktarabilmek için klonlayın

Kötü çözüm 2 : HTML / JSX etrafında bir soyutlama uygulayın, böylece özellikleri dinamik olarak aktarabilirim (öldür beni):

<RadioGroup items=[
    { type: Button, title: 'A' },
    { type: Button, title: 'B' }
]; />

Ve sonra RadioGroupbu düğmeleri dinamik olarak oluşturun.

Bu soru bana yardımcı olmuyor çünkü çocuklarımı ne olduklarını bilmeden render etmem gerekiyor.


Çocuklar keyfi olabiliyorsa, o zaman keyfi çocuklarının yaşadığı RadioGroupbir olaya tepki vermesi gerektiğini nasıl bilebilir? Çocukları hakkında mutlaka bir şeyler bilmek zorundadır.
Ross Allen

1
Genel olarak, sahip olmadığınız bir bileşenin özelliklerini değiştirmek istiyorsanız, onu kullanarak klonlayın React.addons.cloneWithPropsve sahip olmasını istediğiniz yeni özellikleri aktarın. propsdeğişmezdir, bu nedenle yeni bir karma oluşturursunuz, onu mevcut props ile birleştirirsiniz ve yeni karmayı propsalt bileşenin yeni bir örneğine aktarırsınız.
Ross Allen

Yanıtlar:


42

Kullanmanın neden cloneWithPropskötü bir çözüm olduğunu söylediğinizden emin değilim , ama işte onu kullanan çalışan bir örnek.

var Hello = React.createClass({
    render: function() {
        return <div>Hello {this.props.name}</div>;
    }
});

var App = React.createClass({
    render: function() {
        return (
            <Group ref="buttonGroup">
                <Button key={1} name="Component A"/>
                <Button key={2} name="Component B"/>
                <Button key={3} name="Component C"/>
            </Group>
        );
    }
});

var Group = React.createClass({
    getInitialState: function() {
        return {
            selectedItem: null
        };
    },

    selectItem: function(item) {
        this.setState({
            selectedItem: item
        });
    },

    render: function() {
        var selectedKey = (this.state.selectedItem && this.state.selectedItem.props.key) || null;
        var children = this.props.children.map(function(item, i) {
            var isSelected = item.props.key === selectedKey;
            return React.addons.cloneWithProps(item, {
                isSelected: isSelected,
                selectItem: this.selectItem,
                key: item.props.key
            });
        }, this);

        return (
            <div>
                <strong>Selected:</strong> {this.state.selectedItem ? this.state.selectedItem.props.name : 'None'}
                <hr/>
                {children}
            </div>
        );
    }

});

var Button = React.createClass({
    handleClick: function() {
        this.props.selectItem(this);
    },

    render: function() {
        var selected = this.props.isSelected;
        return (
            <div
                onClick={this.handleClick}
                className={selected ? "selected" : ""}
            >
                {this.props.name} ({this.props.key}) {selected ? "<---" : ""}
            </div>
        );
    }

});


React.renderComponent(<App />, document.body);

İşte iş başında gösteren bir jsFiddle .

DÜZENLEME : dinamik sekme içeriğine sahip daha eksiksiz bir örnek: jsFiddle


15
Not: cloneWithProps kullanımdan kaldırılmıştır. Bunun yerine React.cloneElement kullanın.
Chen-Tsu Lin

8
Bu, D3 / Jquery'nin sadece a: değil seçici ile yapabileceği bir şeyi yapmak inanılmaz derecede karmaşık this. React'i kullanmanın dışında herhangi bir gerçek avantajı var mı?
göre öğrenme istatistikleri

Bilginize .. bu "alt durumları ebeveyn durumundan güncellemek" değil, bu çocuk durumunu güncellemek için ebeveyn durumunu güncelleyen çocuktur. Buradaki lakette bir fark var. Bu insanların kafasını karıştırırsa diye.
sksallaj

1
@Incodeveritas haklısın, kardeş ilişkisi, ebeveyn-çocuk ve çocuk-ebeveyn biraz karmaşık. Bu, işleri yapmanın modüler bir yoludur. Ancak JQuery'nin işleri halletmek için hızlı bir hack olduğunu unutmayın, ReactJS işleri bir düzenlemeye göre tutar.
sksallaj

18

Düğmeler durum bilgisiz olmalıdır. Bir düğmenin özelliklerini açıkça güncellemek yerine, sadece Grubun kendi durumunu güncelleyin ve yeniden oluşturun. Grubun oluşturma yöntemi daha sonra düğmeleri işlerken durumuna bakmalı ve "etkin" (veya başka bir şey) yalnızca etkin düğmeye aktarmalıdır.


Genişletmek için, onClick düğmesi veya diğer ilgili yöntemler üst öğeden ayarlanmalıdır, böylece herhangi bir eylem düğmenin kendisinde değil, durumu ayarlamak için üst öğeye geri döner. Bu şekilde düğme, yalnızca ebeveynlerin mevcut durumunun vatansız bir temsilidir.
David Mårtensson

6

Belki benimki garip bir çözüm, ama neden gözlemci kalıbı kullanmıyorsunuz?

RadioGroup.jsx

module.exports = React.createClass({
buttonSetters: [],
regSetter: function(v){
   buttonSetters.push(v);
},
handleChange: function(e) {
   // ...
   var name = e.target.name; //or name
   this.buttonSetters.forEach(function(v){
      if(v.name != name) v.setState(false);
   });
},
render: function() {
  return (
    <div>
      <Button title="A" regSetter={this.regSetter} onChange={handleChange}/>
      <Button title="B" regSetter={this.regSetter} onChange={handleChange} />
    </div>
  );
});

Button.jsx

module.exports = React.createClass({

    onChange: function( e ) {
        // How to modify children properties here???
    },
    componentDidMount: function() {
         this.props.regSetter({name:this.props.title,setState:this.setState});
    },
    onChange:function() {
         this.props.onChange();
    },
    render: function() {
        return (<div onChange={this.onChange}>
            <input element .../>
        </div>);
    }

});

belki başka bir şeye ihtiyacınız var, ancak bunu çok güçlü buldum

Çeşitli görevler için gözlemci kayıt yöntemleri sağlayan bir dış model kullanmayı gerçekten tercih ediyorum


Bir düğümün başka bir düğümde setState'i açıkça çağırması kötü bir biçim değil mi? Daha reaktif bir yol, RadioGroup'taki bazı durumları güncellemek, alt öğelerin props'larını gerektiği gibi güncelleyebileceğiniz başka bir işleme geçişini tetiklemek olabilir mi?
qix

1
Açıkça mı? Hayır, gözlemci sadece bir geri arama çağırır, bu setState olur, ama aslında ben this.setState.bind (this) belirtebilirdim ve bu yüzden yalnızca kendisine bağlanabilirdim. Tamam, dürüst olmam gerekirse, setState'i çağıran ve onu gözlemciye kaydeden yerel bir işlev / yöntem tanımlamalıydım
Daniele Cruciani

1
Bunu yanlış mı okuyorum, yoksa bir nesnede aynı ada sahip iki alan mı var Button.jsx?
JohnK

Button.jsx şunları içerir onChange()veonChange(e)
Josh

ilkini kaldırın, sorudan kesip yapıştırın (ilk soru). Konu bu değil, bu çalışan bir kod değil, olmayacak da.
Daniele Cruciani

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.