React'ta çocuğun durumuna nasıl erişilir?


215

Aşağıdaki yapıya sahibim:

FormEditor- birden fazla FieldEditor tutar FieldEditor- formun bir alanını düzenler ve bu formla ilgili çeşitli değerleri kendi durumuna kaydeder

FormEditor içinde bir düğmeyi tıklattığınızda, tüm FieldEditorbileşenlerden alanlar , durumlarındaki bilgiler ve bunların tümünü FormEditor'da toplayabilmek istiyorum.

Alanlar hakkındaki bilgileri FieldEditordevletin dışında saklamayı düşündüm ve onun FormEditoryerine devlete koydum . Bununla birlikte, bilgilerini değiştirip bilgilerini durumlarında depolarken bileşenlerinin FormEditorher birini dinlemeniz gerekir FieldEditor.

Bunun yerine çocuk devletine erişemez miyim? İdeal mi?


4
"Bunun yerine çocuk devletine erişemez miyim? İdeal mi?" Hayır. Devlet içsel bir şeydir ve dışarıya sızmamalıdır. Bileşeniniz için erişimci yöntemleri uygulayabilirsiniz, ancak bu bile ideal değildir.
Felix Kling

@FelixKling O zaman çocuğun ebeveyn-iletişim için ideal yolun sadece olaylar olduğunu mu söylüyorsunuz?
Moshe Revah

3
Evet, olaylar bir yoludur. Veya Flux'un teşvik ettiği gibi tek yönlü bir veri akışına sahip olun: facebook.github.io/flux
Felix Kling

1
Eğer FieldEditors'yi ayrı olarak kullanmayacaksanız , durumlarını FormEditoriyi seslere kaydetmek . Bu durumda, FieldEditorörnekleriniz, propsform düzenleyicileri tarafından değil, form düzenleyicileri tarafından geçirildiklerine göre oluşturulur state. Daha karmaşık ama esnek bir yol, herhangi bir kap FormEditoraltından geçen ve aralarındaki tüm örnekleri bulan ve bunları bir JSON nesnesine serileştiren bir serileştirici yapmaktır . JSON nesnesi, örneklerin form düzenleyicideki iç içe geçme düzeylerine bağlı olarak isteğe bağlı olarak iç içe yerleştirilebilir (birden fazla düzey).
Meglio

4
Bence React Docs 'Lifting State Up' muhtemelen bunu yapmanın en 'Reacty' yolu
icc97

Yanıtlar:


136

Ayrı ayrı FieldEditors için zaten onChange işleyiciniz varsa, durumu neden sadece FormEditor bileşenine taşıyamayacağınızı ve oradan üst durumu güncelleyecek olan FieldEditors'a bir geri çağrı iletemediğinizi anlamıyorum. Bu benim için bunu yapmanın daha çok React-y yolu gibi görünüyor.

Belki de bununla ilgili bir şey:

const FieldEditor = ({ value, onChange, id }) => {
  const handleChange = event => {
    const text = event.target.value;
    onChange(id, text);
  };

  return (
    <div className="field-editor">
      <input onChange={handleChange} value={value} />
    </div>
  );
};

const FormEditor = props => {
  const [values, setValues] = useState({});
  const handleFieldChange = (fieldId, value) => {
    setValues({ ...values, [fieldId]: value });
  };

  const fields = props.fields.map(field => (
    <FieldEditor
      key={field}
      id={field}
      onChange={handleFieldChange}
      value={values[field]}
    />
  ));

  return (
    <div>
      {fields}
      <pre>{JSON.stringify(values, null, 2)}</pre>
    </div>
  );
};

// To add abillity to dynamically add/remove fields keep the list in state
const App = () => {
  const fields = ["field1", "field2", "anotherField"];

  return <FormEditor fields={fields} />;
};

Orijinal - kanca öncesi versiyon:


2
Bu, onChange için yararlıdır, ancak onSubmit'teki kadar değildir.
190290000 Rublesi Adam

1
Ne demek istediğinizden emin değilim, ancak tek tek alanlardaki onChange işleyicileri her zaman FormEditor bileşeninizde durumunuzda eksiksiz form verilerinin bulunduğundan emin olduğunuz için, onSubmit'iniz sadece bir şey içerecektir myAPI.SaveStuff(this.state);. Biraz daha detaylandırırsanız, belki size daha iyi bir cevap verebilirim :)
Markus-ipse

Diyelim ki her biri kendi doğrulamasına sahip 3 farklı alandan oluşan bir formum var. Göndermeden önce her birini doğrulamak istiyorum. Doğrulama başarısız olursa, hata içeren her alan yeniden oluşturulmalıdır. Bunu önerilen çözümünüzle nasıl yapacağınızı anlayamadım, ama belki de bir yolu var!
190290000 Rublesi Adam

1
RubleMan Orijinal sorudan farklı bir dava gibi görünüyor. Daha fazla ayrıntı ve belki de bazı örnek kod ile yeni bir soru açın ve daha sonra bir göz atabilirim :)
Markus-ipse

Bu yanıtı yararlı bulabilirsiniz : devlet kancalarını kullanır, devletin nerede yaratılması gerektiğini, her tuş vuruşunda formun yeniden oluşturulmasını nasıl önleyeceğinizi, alan doğrulamasının nasıl yapılacağını vb.
Andrew

189

Bir alt bileşenin durumuna nasıl erişebileceğiniz hakkında ayrıntılara girmeden hemen önce, lütfen bu özel senaryoyu ele almak için daha iyi bir çözümle ilgili Markus-ipse'nin yanıtını okuduğunuzdan emin olun .

Gerçekten bir bileşenin alt öğelerinin durumuna erişmek istiyorsanız, refher alt öğeye çağrılan bir özellik atayabilirsiniz . Şimdi referansları uygulamanın iki yolu vardır: React.createRef()refs kullanma ve geri arama.

kullanma React.createRef()

Şu anda React 16.3'ten itibaren referansları kullanmanın önerilen yolu budur (Daha fazla bilgi için dokümanlara bakın ). Daha eski bir sürüm kullanıyorsanız geri arama referanslarıyla ilgili olarak aşağıya bakın.

Üst bileşeninizin yapıcısında yeni bir başvuru oluşturmanız ve daha sonra bu refözelliği öznitelik yoluyla bir alt öğeye atamanız gerekir .

class FormEditor extends React.Component {
  constructor(props) {
    super(props);
    this.FieldEditor1 = React.createRef();
  }
  render() {
    return <FieldEditor ref={this.FieldEditor1} />;
  }
}

Bu tür referanslara erişmek için şunları kullanmanız gerekir:

const currentFieldEditor1 = this.FieldEditor1.current;

Bu, bağlanan bileşenin bir örneğini döndürür, böylece currentFieldEditor1.stateduruma erişmek için kullanabilirsiniz .

Bu referansları bir bileşen (örneğin <div ref={this.divRef} />) yerine bir DOM düğümünde kullanırsanız, this.divRef.currentbileşen örneği yerine temeldeki DOM öğesini döndüreceğini söylemek için hızlı bir not .

Geri Arama Referansları

Bu özellik, ekli bileşene başvuru iletilen bir geri çağırma işlevi alır. Bu geri arama, bileşen takıldıktan veya çıkarıldıktan hemen sonra yürütülür.

Örneğin:

<FieldEditor
    ref={(fieldEditor1) => {this.fieldEditor1 = fieldEditor1;}
    {...props}
/>

Bu örneklerde referans, üst bileşende saklanır. Bu bileşeni kodunuzda çağırmak için şunları kullanabilirsiniz:

this.fieldEditor1

ve sonra this.fieldEditor1.statedevleti almak için kullanın .

Dikkat edilmesi gereken bir nokta, alt bileşene erişmeye çalışmadan önce oluşturduğunuzdan emin olun ^ _ ^

Yukarıdaki gibi, bu başvuruları bir bileşen (örneğin <div ref={(divRef) => {this.myDiv = divRef;}} />) yerine bir DOM düğümünde kullanırsanız, this.divRefbileşen örneği yerine temeldeki DOM öğesini döndürür.

Daha fazla bilgi

React'in ref özelliği hakkında daha fazla bilgi edinmek istiyorsanız , Facebook'tan bu sayfaya göz atın .

Çocuğun "işleri yapmak" için kullanmamanız gerektiğini söyleyen " Ref'leri Aşırı Kullanmayın " bölümünü okuduğunuzdan emin olun state.

Umarım bu yardımcı olur ^ _ ^

Düzenleme: React.createRef()Refs oluşturma yöntemi eklendi . ES5 kodu kaldırıldı.


5
Ayrıca düzgün küçük tidbit, fonksiyonları çağırabilirsiniz this.refs.yourComponentNameHere. İşlevler aracılığıyla durumu değiştirmek için yararlı buldum. Örnek: this.refs.textInputField.clearInput();
Dylan Pierce

7
Dikkat edin (dokümanlardan): Refs, belirli bir alt örneğe Reaktif propsve state. Bununla birlikte, uygulamanızdan veri akışı için gitmek için soyutlamanız olmamalıdır. Varsayılan olarak, Reaktif veri akışını kullanın ve refdoğal olarak reaktif olmayan kullanım durumları için kaydedin .
Lynn

2
Sadece yapıcı içinde tanımlanan durumu alıyorum (güncel değil)
Vlado Pandžić

3
Şimdi refs kullanmak, 'Kontrollü Bileşenler' kullanılması önerisiyle ' Kontrolsüz Bileşenler ' olarak sınıflandırılmıştır
icc97

Alt connectedbileşen bir Redux bileşeniyse bunu yapmak mümkün müdür ?
2018'de

7

Onun 2020 ve birçoğu buraya benzer bir çözüm arıyor ama Hooks ile (Onlar harika!) Ve kod temizliği ve sözdizimi açısından en son yaklaşımlarla gelecekler .

Önceki yanıtların da belirttiği gibi, bu tür bir soruna en iyi yaklaşım devleti alt öğenin dışında tutmaktır fieldEditor. Bunu çeşitli şekillerde yapabilirsiniz.

En "karmaşık", hem ebeveynlerin hem de çocukların erişebileceği ve değiştirebileceği küresel bağlam (durum) ile ilgilidir. Bileşenler ağaç hiyerarşisinde çok derin olduğunda harika bir çözümdür ve bu nedenle her düzeyde sahne göndermek pahalıya mal olur.

Bu durumda bence buna değmez ve daha basit yaklaşım bize sadece güçlü olanı kullanarak istediğimiz sonuçları getirecektir React.useState().

React.useState () kancalı yaklaşım, Class bileşenlerinden çok daha basit

Dediğimiz gibi, değişiklikler ile ilgileneceğiz ve alt bileşenimizin verilerini fieldEditorebeveynimizde saklayacağız fieldForm. Bunu yapmak için, değişiklikleri fieldFormdevlete uygulayacak ve uygulayacak fonksiyona bir referans göndereceğiz , bunu aşağıdakilerle yapabilirsiniz:

function FieldForm({ fields }) {
  const [fieldsValues, setFieldsValues] = React.useState({});
  const handleChange = (event, fieldId) => {
    let newFields = { ...fieldsValues };
    newFields[fieldId] = event.target.value;

    setFieldsValues(newFields);
  };

  return (
    <div>
      {fields.map(field => (
        <FieldEditor
          key={field}
          id={field}
          handleChange={handleChange}
          value={fieldsValues[field]}
        />
      ))}
      <div>{JSON.stringify(fieldsValues)}</div>
    </div>
  );
}

React.useState({})Konum 0'ın çağrıda belirtilen değer (bu durumda Boş nesne) ve konum 1'in değeri değiştiren işleve başvuru olduğu bir dizi döndüreceğini unutmayın .

Şimdi alt bileşenle, FieldEditorbir return ifadesiyle bir işlev oluşturmanız bile gerekmez, bir ok işlevine sahip yalın bir sabit yapacaktır!

const FieldEditor = ({ id, value, handleChange }) => (
  <div className="field-editor">
    <input onChange={event => handleChange(event, id)} value={value} />
  </div>
);

Aaaave bitti, başka bir şey yok, sadece bu iki balçık fonksiyonel bileşen ile nihai hedefimiz çocuk FieldEditordeğerine "erişiyor" ve ebeveynimizde gösteriyor.

5 yıl önce kabul edilen cevabı kontrol edebilir ve Hooks'un tepki kodunu nasıl daha ince yaptığını görebilirsiniz (Çok fazla!).

Umarım cevabım Hooks hakkında daha fazla bilgi edinmenize ve anlamanıza yardımcı olur ve burada çalışan bir örneği kontrol etmek istiyorsanız .


4

Şimdi, FormEditor alt öğesi olan InputField'ın durumuna erişebilirsiniz.

Temel olarak, girdi alanının (alt öğesinin) durumunda bir değişiklik olduğunda, değeri olay nesnesinden alıyoruz ve sonra bu değeri Ana öğedeki durumun ayarlandığı Ebeveyne iletiyoruz.

Düğme tıklandığında sadece Giriş alanlarının durumunu yazdırıyoruz.

Buradaki kilit nokta, Giriş Alanının kimliğini / değerini elde etmek ve yeniden kullanılabilir alt Giriş alanlarını oluştururken Giriş Alanı'nda öznitelik olarak ayarlanan işlevleri çağırmak için destekleri kullanmamızdır.

class InputField extends React.Component{
  handleChange = (event)=> {
    const val = event.target.value;
    this.props.onChange(this.props.id , val);
  }

  render() {
    return(
      <div>
        <input type="text" onChange={this.handleChange} value={this.props.value}/>
        <br/><br/>
      </div>
    );
  }
}       


class FormEditorParent extends React.Component {
  state = {};
  handleFieldChange = (inputFieldId , inputFieldValue) => {
    this.setState({[inputFieldId]:inputFieldValue});
  }
  //on Button click simply get the state of the input field
  handleClick = ()=>{
    console.log(JSON.stringify(this.state));
  }

  render() {
    const fields = this.props.fields.map(field => (
      <InputField
        key={field}
        id={field}
        onChange={this.handleFieldChange}
        value={this.state[field]}
      />
    ));

    return (
      <div>
        <div>
          <button onClick={this.handleClick}>Click Me</button>
        </div>
        <div>
          {fields}
        </div>
      </div>
    );
  }
}

const App = () => {
  const fields = ["field1", "field2", "anotherField"];
  return <FormEditorParent fields={fields} />;
};

ReactDOM.render(<App/>, mountNode);

7
Bunu iptal edebileceğimden emin değilim. Burada @ Markus-ipse tarafından cevaplanmayan ne diyorsunuz?
Alexander Bird

3

Önceki yanıtlar kaydedildikçe, durumu bir üst bileşene taşımaya ve alt öğelerine iletilen geri çağrılarla durumu değiştirmeye çalışın.

İşlevsel bir bileşen (kancalar) olarak bildirilen bir alt duruma gerçekten erişmeniz gerekiyorsa , üst bileşende bir ref bildirebilir , ardından çocuğa bir ref özniteliği olarak iletebilirsiniz , ancak React.forwardRef'i kullanmanız gerekir ve sonra kanca , üst bileşende arayabileceğiniz bir işlevi bildirmek için useImperativeHandle çağrısını kullanır.

Aşağıdaki örneğe bir göz atın:

const Parent = () => {
    const myRef = useRef();
    return <Child ref={myRef} />;
}

const Child = React.forwardRef((props, ref) => {
    const [myState, setMyState] = useState('This is my state!');
    useImperativeHandle(ref, () => ({getMyState: () => {return myState}}), [myState]);
})

Daha sonra şu öğeyi arayarak myState'i Ana bileşende alabilmelisiniz: myRef.current.getMyState();

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.