React'ta ebeveynin durumu nasıl güncellenir?


351

Yapım aşağıdaki gibi görünüyor:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Bileşen 3, Bileşen 5'in durumuna bağlı olarak bazı verileri görüntülemelidir. Sahne öğeleri değiştirilemediğinden, durumunu Bileşen 1'e kaydedip iletemiyorum, değil mi? Ve evet, redux hakkında okudum, ama kullanmak istemiyorum. Umarım bunu sadece tepki ile çözmek mümkündür. Yanlış mıyım?


20
super-easy: parent-setState-Function işlevini child-bileşenine iletir: <MyChildComponent setState = {p => {this.setState (p)}} /> Alt bileşende bunu this.props üzerinden arayın. setState ({myObj, ...});
Marcel Ennix

@MarcelEnnix, Yorumunuz zamanımı kurtarıyor. Teşekkürler.
Dinith Minura

<MyChildComponent setState={(s,c)=>{this.setState(s, c)}} />bu hack'i kullanacaksanız geri aramayı desteklediğinizden emin olun.
Barkermn01

4
Ebeveynin durumunu ayarlamak için geri arama yapmak bakım sorunlarına yol açabilecek gerçekten kötü bir uygulamadır. Kapsüllemeyi keser ve bileşen 2 4 ve 5'i sıkıca 1'e bağlar. Bu yolda yürürseniz, bu alt bileşenlerin hiçbirini başka bir yerde kullanamazsınız. Özel bileşenleriniz olması daha iyidir, böylece alt bileşenler bir şey olduğunda olayları tetikleyebilir, daha sonra üst bileşen bu olayı düzgün bir şekilde işleyebilir.
Pato Loco

@MarcelEnnix, neden süslü parantez this.setState(p)?
Onlarsız

Yanıtlar:


679

Çocuk-ebeveyn iletişimi için, durumu ebeveynten çocuğa ayarlayan bir işlevi geçmelisiniz, örneğin

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: 'some value'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

Bu şekilde, çocuk ebeveynlerle birlikte, sahne modlarından geçirilen bir işlevin çağrısıyla güncelleştirilebilir.

Ancak, bileşenlerinizin yapısını yeniden düşünmeniz gerekecek, çünkü anladığım kadarıyla, 5 ve 3 bileşenleri ilişkili değildir.

Olası bir çözüm, bunları hem bileşen 1 hem de 3'ün durumunu içerecek olan daha üst düzey bir bileşene sarmaktır. Bu bileşen, alt düzey durumunu sahne aracılığıyla ayarlayacaktır.


6
neden yalnızca durumu ayarlayan işleyici işlevine değil, this.handler = this.handler.bind (this) öğesine ihtiyacınız var?
chemook78

35
ES6 React sınıfları yöntemlerindeki @ chemook78, sınıfa otomatik olarak bağlanmaz. Biz katmayan Yani this.handler = this.handler.bind(this), yapıcı içinde thishandlerişlev işlev kapatılması değil, sınıf referans alır. Yapıcıdaki tüm işlevlerinizi bağlamak istemiyorsanız, ok işlevlerini kullanarak bununla başa çıkmanın iki yolu daha vardır. Sadece gibi tıklama işleyicisi yazabilirsiniz onClick={()=> this.setState(...)}, ya da burada anlatıldığı gibi ok fonksiyonları ile birlikte mülkiyet olan başlatıcı kullanabilirsiniz babeljs.io/blog/2015/06/07/react-on-es6-plus "Ok fonksiyonları" altında
Ivan

1
Tamb

5
Her şey mantıklı, neden e.preventDefault? ve bu jquery gerektiriyor mu?
Vincent Buscarello

1
Kısa soru, bu çocuk içinde yerel bir devletin görevlendirilmesine izin vermiyor mu?
ThisGuyCantEven

50

Çocuktan üst bileşene onClick işlev bağımsız değişkenini iletmek için aşağıdaki çalışma çözümünü buldum:

Bir yöntemi () geçiren sürüm

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: ' + someArg);
            this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

JSFIDDLE'a bak

Ok işlevini geçen sürüm

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component { 
    constructor(props) {
        super(props);
    }

    handleToUpdate = (someArg) => {
            alert('We pass argument from Child to Parent: ' + someArg);
    }

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

JSFIDDLE'a bak


Bu güzel! Bu parçayı açıklayabilir misin: <ChildB handleToUpdate = {handleToUpdate.bind(this)} />Neden tekrar bağlanmak zorundaydın?
Dane

@Dane - bunun bağlamını ebeveyn olarak bağlamalısınız, böylece thisçocuğun içinde çağrıldığında çocuğun thisdeğil ebeveynin durumunu ifade eder. Bu en iyi kapatma!
Casey

@Casey ama bunu yapıcıda yapmıyor muyuz? Ve bu yeterli değil mi ??
Dane

Tamamen haklısın! Onu özledim. Evet, zaten yapıcıda yaptıysanız o zaman gitmek iyidir!
Casey

Bir efsane dostun! Bu, devlet borsalarını işlemek için ana bileşenler oluşturmak zorunda kalmadan bileşenleri güzel bir şekilde kendi içinde tutacaktır
adamj

13

Bana kendi sorunum fikrini temelde ok işleviyle değiştirmesi ve çocuk bileşeninden param geçirmesi fikrini verdiğim için en üstteki cevaba teşekkür etmek istiyorum:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

Umarım birine yardımcı olur.


Doğrudan aramada ok işlevi hakkında özel olan nedir?
Ashish Kamble

@AshishKamble thisin arrow işlevleri ebeveynin bağlamını (yani Parentsınıf) ifade eder .
CPHPython

Bu yinelenen bir cevaptır. Kabul edilen yanıta bir yorum ekleyebilir ve sınıfta ok işlevini kullanmak için bu deneysel özellik hakkında bahsedebilirsiniz .
Arashsoft

10

İşlevleri aktarma ile ilgili cevabı seviyorum, çok kullanışlı bir teknik.

Flip tarafında bunu pub / sub kullanarak veya Flux'un yaptığı gibi bir varyant, bir dağıtım programı kullanarak da başarabilirsiniz . Teori çok basittir, bileşen 5'in bileşen 3'ün dinlediği bir mesaj göndermesini sağlayın. Bileşen 3 daha sonra yeniden oluşturmayı tetikleyen durumunu günceller. Bu, bakış açınıza bağlı olarak bir anti-desen olabilecek veya olmayabilecek durumlu bileşenler gerektirir. Ben şahsen onlara karşıyım ve daha çok yukarıdan aşağıya bir şeylerin gönderilmesini ve durumunu değiştirmesini tercih eder (Redux bunu yapar, ancak ek terminoloji ekler).

import { Dispatcher } from flux
import { Component } from React

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

Flux ile dispatcher paketleri çok basittir, yalnızca geri çağrıları kaydeder ve herhangi bir dağıtım gerçekleştiğinde, dağıtımdaki içeriklerden geçerek onları çağırır (yukarıdaki üstteki örnekte payload, dağıtım ile hayır , sadece bir mesaj kimliği yoktur). Bunu sizin için daha anlamlı hale getirirse, geleneksel pub / sub'e (örneğin olaylardan EventEmitter'ı veya başka bir sürümü kullanarak) kolayca adapte edebilirsiniz.


Reaksiyonlarım bileşenleri resmi bir öğreticide olduğu gibi tarayıcıda "çalışıyor" ( facebook.github.io/react/docs/tutorial.html ) Flux'u browserify ile dahil etmeye çalıştım, ancak tarayıcı diyor ki Dispatcher bulunamadı :(
wklm

2
Kullandığım sözdizimi, transpilasyon gerektiren ES2016 modül sözdizimiydi ( Babel kullanıyorum , ancak diğerleri var, babelify dönüşümü browserify ile kullanılabilir),var Dispatcher = require( 'flux' ).Dispatcher
Matt Styles

8

Paracl ile alt öğeye onClick işlev bağımsız değişkenini iletmek için aşağıdaki çalışma çözümünü buldum:

ebeveyn sınıfı:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

çocuk sınıfı:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }

2
Herkes bunun kabul edilebilir bir çözüm olup olmadığını söyleyebilir (özellikle parametreyi önerildiği gibi iletmekle ilgilenir).
ilans

param1sadece atadığı konsolda görüntülenmez her zaman atartrue
Ashish Kamble

Çözümün kalitesi ile konuşamam, ama bu benim için başarıyla geçiyor.
James

6

Herhangi bir seviyede çocukla ebeveyn arasında iletişim kurmanız gerektiğinde, bağlamdan yararlanmak daha iyidir . Ana bileşende çocuk tarafından çağrılabilecek bağlamı tanımlayın, örneğin

Vaka bileşeninizdeki üst bileşende 3

static childContextTypes = {
        parentMethod: React.PropTypes.func.isRequired
      };

       getChildContext() {
        return {
          parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
        };
      }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

Şimdi alt bileşende (sizin durumunuzdaki bileşen 5), bu bileşene üst öğesinin içeriğini kullanmak istediğini söyleyin.

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >   

            <Text> update state in parent component </Text>              

      </TouchableHighlight>
)}

Demo projesini repo'da bulabilirsiniz


Bu cevabı anlayamıyorum, hakkında daha fazla bilgi verebilir misiniz
Ashish Kamble

5

Tepki Tek Yönlü Veri Akışı'nı teşvik ettiği için yalnızca ebeveynlerden çocuğa veri aktarabileceğimiz gibi görünüyor, ancak "alt bileşeninde" bir şey olduğunda ebeveyn güncellemesini yapmak için genellikle "geri arama işlevi" olarak adlandırılan şeyi kullanıyoruz.

Ebeveynde tanımlanan işlevi çocuğa "sahne" olarak geçiririz ve bu işlevi alt bileşende tetikleyen alt öğeden çağırırız.


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

Bu örnekte, işlevi doğrudan Çocuğuna ileterek SubChild -> Çocuk -> Ebeveyn'den veri geçirebiliriz.


4

Bu sayfadan birçok kez en yüksek dereceli bir cevap kullandım, ancak React öğrenirken, bunu yapmanın daha iyi bir yolunu, sahne içinde bağlayıcı olmadan ve satır içi işlevi olmadan buldum.

Sadece buraya bak:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

Anahtar bir ok işlevindedir:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

Daha fazlasını buradan okuyabilirsiniz . Bu birileri için yararlı olacağını umuyoruz =)


Bu benim için çok daha mantıklı. Teşekkür ederim!
Brett Rowberry

3

-ParentComponent durumunu ve ParentComponent durumunu güncellemek için handleInputChange yöntemiyle oluşturabiliriz. ChildComponent bileşenini içe aktardığımızda üst öğeden alt öğeye iki sahne geçiririz. HandleInputChange işlevi ve sayısı.

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • Şimdi ChildComponent dosyasını oluşturuyoruz ve ChildComponent.jsx olarak kaydediyoruz. Bu bileşen vatansızdır, çünkü alt bileşenin bir durumu yoktur. Sahne tipi kontrolü için prop-tip kütüphanesini kullanıyoruz.

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;

Çocuğun ebeveyninin pervanesini etkileyen bir çocuğu olduğunda bu nasıl çalışır?
Bobort

3

Aynı senaryo her yere yayılmamışsa, React'in bağlamını kullanabilirsiniz, özellikle durum yönetimi kitaplıklarının getirdiği tüm ek yükü tanıtmak istemiyorsanız. Ayrıca, öğrenmesi daha kolaydır. Ancak dikkatli olun, aşırı kullanabilirsiniz ve kötü kod yazmaya başlayabilirsiniz. Temel olarak, bir kapsayıcı bileşen tanımlarsınız (bu durumu sizin için tutacak ve tutacak), bu veri parçasını çocuklarına yazmak / okumakla ilgilenen tüm bileşenleri çocuklara dönüştürür (mutlaka çocukları değil)

https://reactjs.org/docs/context.html

Bunun yerine düz Reaksiyonu düzgün bir şekilde kullanabilirsiniz.

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />

Bileşen 1'e kadar doSomethingAbout5'i geçin

    <Component1>
        <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
        <Component5 propThatDependsOn5={this.state.somethingAbout5}/>
    <Component1/>

Bu yaygın bir sorunsa, uygulamanın tüm durumunu başka bir yere taşımayı düşünmeye başlamalısınız. Birkaç seçeneğiniz var, en yaygın olanları:

https://redux.js.org/

https://facebook.github.io/flux/

Temel olarak, bileşeninizdeki uygulama durumunu yönetmek yerine, durumu güncellemek için bir şey olduğunda komutlar gönderirsiniz. Bileşenler, durumu bu kaptan da alır, böylece tüm veriler merkezileştirilir. Bu, artık yerel durumu kullanamayacağınız anlamına gelmez, ancak bu daha gelişmiş bir konudur.


2

ana bileşeni güncellemek istiyorsanız,

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

        render(){
          return (
              <ChildComponent onChange={this.handler} />
           )
       }
   }


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

Burada onChange, örneğine bağlı "handler" yöntemine sahip bir özelliktir. props bağımsız değişkeninde onChange özelliği üzerinden almak için yöntem işleyicisini Child sınıfı bileşenine geçtik.

OnChange özelliği aşağıdaki gibi bir sahne nesnesinde ayarlanır:

props ={
onChange : this.handler
}

ve alt bileşene geçti

Böylece Child bileşeni, props.onChange gibi props nesnesindeki ad değerine erişebilir.

Bu render sahne kullanımı ile yapılır.

Şimdi Child bileşeninde, proc argüman nesnesinde onChnge aracılığıyla kendisine gönderilen işleyici yöntemini çağırmak için ayarlanmış bir onclick olayı olan bir "Click" düğmesi vardır. Şimdi this.props.onChange in Child, çıkış yöntemini Ana sınıfta barındırır Referans ve krediler: Bitler ve Parçalar


Üzgünüm .. gecikme için, Burada onChange kendi örneğine bağlı "işleyici" yöntemi ile bir özniteliğidir. props bağımsız değişkeninde onChange özelliği üzerinden almak için yöntem işleyicisini Child sınıfı bileşenine geçtik. OnChange özniteliği şöyle bir sahne nesnesinde ayarlanır: props = {onChange: this.handler} ve alt bileşene aktarılır. Böylece Child bileşeni, props.onChange gibi props.onChange gibi ad nesnesinin değerine erişebilir. render sahne kullanımı. Referans ve krediler: [ blog.bitsrc.io/…
NT

0

Bu şekilde yapıyorum.

type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
    constructor(props: ParentProps) {
        super(props)
        this.state = { someValue: 0 }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, someValue: value})
    }

    render() {
        return <div>
            <Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
            <p>Value: {this.state.someValue}</p>
        </div>
    }
}

type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
    constructor(props: ChildProps) {
        super(props)
        this.state = { anotherValue: this.props.defaultValue }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, anotherValue: value})
        this.props.changeFunction(value)
    }

    render() {
        return <div>
            <input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
        </div>
    }
}

0

Yukarıda verilen cevapların çoğu React.Component tabanlı tasarımlar içindir. useStateReact kitaplığının son yükseltmelerinde kullanıyorsanız , bu yanıtı izleyin


-3
<Footer 
  action={()=>this.setState({showChart: true})}
/>

<footer className="row">
    <button type="button" onClick={this.props.action}>Edit</button>
  {console.log(this.props)}
</footer>

Try this example to write inline setState, it avoids creating another function.

Performans sorununa neden olur: stackoverflow.com/q/36677733/3328979
Arashsoft
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.