Redux'da eşzamansız eylemler gerçekleştiren kalıcı bir iletişim kutusunu nasıl görüntüleyebilirim?


240

Bazı durumlarda bir onaylama iletişim kutusu göstermesi gereken bir uygulama oluşturuyorum.

Diyelim ki bir şeyi kaldırmak istiyorum, o zaman deleteSomething(id)bazı redüktör o olayı yakalayacak ve göstermek için iletişim redüktörünü dolduracak gibi bir eylem göndereceğim .

Şüphem bu diyalog gönderildiğinde gelir.

  • Bu bileşen, gönderilen ilk eyleme göre uygun eylemi nasıl gönderebilir?
  • Eylem oluşturucu bu mantığı işlemeli mi?
  • Redüktörün içine eylem ekleyebilir miyiz?

Düzenle:

daha açık hale getirmek için:

deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)

createThingB(id) => Show dialog with Questions => createThingBRemotely(id)

Bu yüzden iletişim kutusunu yeniden kullanmaya çalışıyorum. Diyaloğu göstermek / gizlemek sorun değildir, çünkü bu redüktörde kolayca yapılabilir. Ne belirtmeye çalışıyorum sol tarafta akışı başlatan eyleme göre eylem sağ taraftan nasıl gönderilir.


1
Sizin durumunuzda iletişim kutusunun (gizle / göster) yerel olduğunu düşünüyorum. Tepki durumunu gösterme / gizleme iletişim kutusunu yönetmek için kullanmayı tercih ederim. Bu şekilde, "ilk eyleme göre uygun eylem" sorusu ortadan kalkacaktır.
Ming

Yanıtlar:


516

Önerdiğim yaklaşım biraz ayrıntılı ama karmaşık uygulamalara oldukça iyi ölçeklendiğini buldum. Bir model göstermek istediğinizde, hangi modeli görmek istediğinizi açıklayan bir işlem başlatın :

Kipi Göstermek için Bir Eylem Gönderme

this.props.dispatch({
  type: 'SHOW_MODAL',
  modalType: 'DELETE_POST',
  modalProps: {
    postId: 42
  }
})

(Dizeler elbette sabit olabilir; basitlik için satır içi dizeler kullanıyorum.)

Kalıcı Durumu Yönetmek İçin Bir Redüktör Yazma

Ardından, yalnızca bu değerleri kabul eden bir redüktörünüz olduğundan emin olun:

const initialState = {
  modalType: null,
  modalProps: {}
}

function modal(state = initialState, action) {
  switch (action.type) {
    case 'SHOW_MODAL':
      return {
        modalType: action.modalType,
        modalProps: action.modalProps
      }
    case 'HIDE_MODAL':
      return initialState
    default:
      return state
  }
}

/* .... */

const rootReducer = combineReducers({
  modal,
  /* other reducers */
})

Harika! Şimdi, bir eylem gönderirken, state.modalo anda görünür mod penceresi hakkında bilgileri içerecek şekilde güncellenecektir.

Kök Kalıcı Bileşenin Yazılması

Bileşen hiyerarşinizin köküne <ModalRoot>, Redux deposuna bağlı bir bileşen ekleyin . state.modalUygun bir mod bileşenini dinleyecek ve görüntüleyecektir state.modal.modalProps.

// These are regular React components we will write soon
import DeletePostModal from './DeletePostModal'
import ConfirmLogoutModal from './ConfirmLogoutModal'

const MODAL_COMPONENTS = {
  'DELETE_POST': DeletePostModal,
  'CONFIRM_LOGOUT': ConfirmLogoutModal,
  /* other modals */
}

const ModalRoot = ({ modalType, modalProps }) => {
  if (!modalType) {
    return <span /> // after React v15 you can return null here
  }

  const SpecificModal = MODAL_COMPONENTS[modalType]
  return <SpecificModal {...modalProps} />
}

export default connect(
  state => state.modal
)(ModalRoot)

Burada ne yaptık? ModalRootakımını okur modalTypeve modalPropsgelenstate.modal hangi bağlı olduğu, ve karşılık gelen bir bileşen gibi işler DeletePostModalya da ConfirmLogoutModal. Her model bir bileşendir!

Belirli Modal Bileşenleri Yazma

Burada genel bir kural yok. Bunlar sadece eylemleri gönderebilen, mağaza durumundan bir şeyler okuyabilen ve sadece modals olabilen React bileşenleridir. .

Örneğin, DeletePostModalşöyle görünebilir:

import { deletePost, hideModal } from '../actions'

const DeletePostModal = ({ post, dispatch }) => (
  <div>
    <p>Delete post {post.name}?</p>
    <button onClick={() => {
      dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
      })
    }}>
      Yes
    </button>
    <button onClick={() => dispatch(hideModal())}>
      Nope
    </button>
  </div>
)

export default connect(
  (state, ownProps) => ({
    post: state.postsById[ownProps.postId]
  })
)(DeletePostModal)

DeletePostModalO Yayın başlığını görüntülemek ve herhangi bir bağlı bileşen gibi çalışır böylece deposuna bağlı: o dahil eylemleri sevk edebilirhideModal kendisini gizlemek için gerektiğinde.

Sunum Bileşenini Çıkarma

Her "özel" mod için aynı yerleşim mantığını kopyalayıp yapıştırmak zor olur. Ama bileşenleriniz var, değil mi? Böylece bir sunum çıkarabilirsiniz <Modal> nasıl göründükleri yapmak özellikle neyi modals bilmez bileşeni ama kolları.

Ardından, DeletePostModaloluşturma için kullanabileceğiniz gibi belirli modüller :

import { deletePost, hideModal } from '../actions'
import Modal from './Modal'

const DeletePostModal = ({ post, dispatch }) => (
  <Modal
    dangerText={`Delete post ${post.name}?`}
    onDangerClick={() =>
      dispatch(deletePost(post.id)).then(() => {
        dispatch(hideModal())
      })
    })
  />
)

export default connect(
  (state, ownProps) => ({
    post: state.postsById[ownProps.postId]
  })
)(DeletePostModal)

Bir dizi sahne bulmak size kalmış <Modal>Başvurunuzda kabul edebilecek , ancak birkaç çeşit modalın (örneğin bilgi modal, onay modal, vb.) Ve onlar için çeşitli stiller olabileceğini düşünürüm.

Erişilebilirlik ve Tıklama Dışında veya Kaçış Anahtarında Gizleme

Modals ile ilgili son önemli kısım, genellikle kullanıcı dışarıya tıkladığında veya Escape tuşuna bastığında onları gizlemek istiyoruz.

Size bunu uygulamakla ilgili tavsiyelerde bulunmak yerine, sadece kendiniz uygulamamanızı öneririm. Erişilebilirliği göz önünde bulundurarak doğru olmak zordur.

Bunun yerine, erişilebilir bir kullanıma hazır mod bileşenini kullanmanızı öneririm react-modal. Tamamen özelleştirilebilir, içine istediğiniz her şeyi koyabilirsiniz, ancak kör insanların yine de modalınızı kullanabilmeleri için erişilebilirliği doğru şekilde işler.

Uygulamalarınız için özel sahne kabul eden ve alt düğmeler veya başka içerikler üreten react-modalkendi başınıza bile sarmalayabilirsiniz <Modal>. Hepsi sadece bileşenler!

Diğer Yaklaşımlar

Bunu yapmanın birden fazla yolu var.

Bazı insanlar bu yaklaşımın ayrıntı düzeyini sevmezler ve “portal” adı verilen bir teknikle bileşenlerinin içinde<Modal> oluşturabilecekleri bir bileşeni tercih ederler . Portallar, aslında bir bileşeni kendi içinizde oluşturmanıza izin verir DOM'da önceden belirlenmiş bir yerde renderlenecek, bu da modals için çok uygun.

Aslında react-modaldaha önce bağlandım, bunu dahili olarak çok teknik olarak yapıyorum, hatta yukarıdan oluşturmanıza bile gerek yok. Göstermek istediğim modalı gösteren bileşenden ayırmayı hala güzel buluyorum, ancak react-modaldoğrudan bileşenlerinizden de kullanabilir ve yukarıda yazdıklarımın çoğunu atlayabilirsiniz.

Her iki yaklaşımı da düşünmenizi, onlarla denemenizi ve uygulamanız ve ekibiniz için en uygun olanı seçmenizi öneririm.


35
Önerebileceğim bir şey, redüktörün itilebilecek ve patlatılabilecek bir moda listesi tutmasını sağlamaktır. Göründüğü kadar aptalca, tasarımcıların / ürün türlerinin bir moddan bir mod açmamı istediği durumlarla sürekli olarak karşılaşıyorum ve kullanıcıların "geri dönmelerine" izin vermek güzel.
Kyle

9
Evet, kesinlikle, bu Redux'un inşasını kolaylaştıran bir şey, çünkü durumunuzu bir dizi olarak değiştirebilirsiniz. Şahsen, tersi, modals'ın özel olmasını isteyen tasarımcılar ile çalıştım, bu yüzden yazdığım yaklaşım yanlışlıkla yuvalamayı çözdü. Ama evet, her iki yöne de sahip olabilirsiniz.
Dan Abramov

4
Benim tecrübeme göre şunu söyleyebilirim: mod yerel bir bileşenle ilgiliyse (silme onayı modunun sil düğmesiyle ilgili olması gibi), bir portal kullanmak daha kolaydır, aksi takdirde redux eylemleri kullanın. @Kyle ile hemfikir olun, bir moddan bir mod açabilmelisiniz. Ayrıca, varsayılan olarak portallarla birlikte çalışır, çünkü gövdeyi belgelemek için eklenirler, böylece portallar birbirleriyle güzelce istiflenir (z-endeksi ile her şeyi karıştırıncaya kadar: p)
Sebastien Lorber

4
@ DanAbramov, çözümünüz harika, ama küçük bir sorunum var. Önemli bir şey değil. Projede Material-ui kullanıyorum, modal kaparken solmaz animasyonu "oynatmak" yerine onu kapattı. Muhtemelen bir tür gecikme mi yapmanız gerekiyor? Yoksa ModalRoot'un içindeki her modal listesi olarak mı saklıyorsunuz? Öneriler?
gcerar

7
Bazen mod kapandıktan sonra belirli işlevleri çağırmak istiyorum (örneğin, modun içindeki giriş alanı değerlerine sahip işlevleri çağırın). Bu işlevleri modalPropseyleme geçirdim . Bu, durumu serileştirilebilir tutma kuralını ihlal eder. Bu sorunun üstesinden nasıl gelebilirim?
chmanie

98

Güncelleme : React 16.0 bağlantı yoluyla portalları tanıttıReactDOM.createPortal

Güncelleme : React'ın sonraki sürümleri (Fiber: muhtemelen 16 veya 17) portallar oluşturmak için bir yöntem içerir: ReactDOM.unstable_createPortal() link


Portalları kullan

Dan Abramov cevap ilk kısmı iyi, ama çok sayıda kazan levha içerir. Söylediği gibi portalları da kullanabilirsiniz. Bu fikri biraz daha genişleteceğim.

Bir portalın avantajı, açılır pencerenin ve düğmenin, props kullanarak çok basit ebeveyn / çocuk iletişimi ile Tepki ağacına çok yakın kalmasıdır: portallarla zaman uyumsuz eylemleri kolayca halledebilir veya ebeveynin portalı özelleştirmesine izin verebilirsiniz.

Portal nedir?

Bir portal document.body, React ağacınızda derinden yuvalanmış bir öğenin içinde doğrudan oluşturmanıza izin verir .

Fikir, örneğin aşağıdaki React ağacını bedeninize dönüştürmenizdir:

<div className="layout">
  <div className="outside-portal">
    <Portal>
      <div className="inside-portal">
        PortalContent
      </div>
    </Portal>
  </div>
</div>

Ve çıktı olarak alırsınız:

<body>
  <div class="layout">
    <div class="outside-portal">
    </div>
  </div>
  <div class="inside-portal">
    PortalContent
  </div>
</body>

inside-portalDüğüm içine tercüme edilmiştir <body>yerine normal, iç içe geçmiş çok yerin.

Portal ne zaman kullanılır?

Portal özellikle mevcut React bileşenlerinizin üstüne çıkması gereken öğeleri görüntülemek için kullanışlıdır: açılır pencereler, açılır listeler, öneriler, etkin noktalar

Neden portal kullanmalı?

Artık z-endeksi problemi yok : bir portal, görüntülemenize izin veriyor <body>. Bir pop-up veya açılır menü görüntülemek istiyorsanız, z-endeksi sorunlarıyla mücadele etmek istemiyorsanız bu gerçekten güzel bir fikirdir. Portal öğeleri ekleme document.bodysırasına göre yapılır, yani oynatılmadığınız sürece z-indexvarsayılan davranış, portalları üst üste, montaj sırasına göre yığınlamak olacaktır. Uygulamada, bir pop-up'ı başka bir pop-up içinden güvenli bir şekilde açabileceğiniz ve düşünmeye gerek kalmadan 2. pop-up'ın ilkin üstünde görüntüleneceğinden emin olmanız anlamına gelir z-index.

Uygulamada

En basit: yerel React durumunu kullanın: Eğer basit bir silme onayı açılır penceresi için, Redux kazan plakasına sahip olmaya değmez, o zaman bir portal kullanabilirsiniz ve kodunuzu büyük ölçüde basitleştirir. Etkileşimin çok yerel olduğu ve aslında bir uygulama detayı olduğu böyle bir kullanım durumunda, gerçekten sıcak yeniden yükleme, zaman yolculuğu, eylem günlüğü ve Redux'un size sağladığı tüm faydaları önemsiyor musunuz? Şahsen ben bu durumda yerel devleti kullanmıyorum ve kullanıyorum. Kod şöyle basitleşir:

class DeleteButton extends React.Component {
  static propTypes = {
    onDelete: PropTypes.func.isRequired,
  };

  state = { confirmationPopup: false };

  open = () => {
    this.setState({ confirmationPopup: true });
  };

  close = () => {
    this.setState({ confirmationPopup: false });
  };

  render() {
    return (
      <div className="delete-button">
        <div onClick={() => this.open()}>Delete</div>
        {this.state.confirmationPopup && (
          <Portal>
            <DeleteConfirmationPopup
              onCancel={() => this.close()}
              onConfirm={() => {
                this.close();
                this.props.onDelete();
              }}
            />
          </Portal>
        )}
      </div>
    );
  }
}

Basit: Yine de Redux durumunu kullanabilirsiniz : Gerçekten istiyorsanız , gösterilip gösterilmeyeceğini connectseçmek için de kullanabilirsiniz DeleteConfirmationPopup. Portal, React ağacınızda derinden yuvalanmış olarak kaldığından, bu portalın davranışını özelleştirmek çok basittir, çünkü ebeveyniniz portala sahne geçebilir. Portalları kullanmıyorsanız, genellikle pop-up'larınızı React ağacınızın en üstünde,z-indexve genellikle "kullanım durumuna göre oluşturduğum genel DeleteConfirmationPopup'ı nasıl özelleştiririm" gibi şeyler hakkında düşünmek zorundayım. Ve genellikle bu soruna, iç içe onaylama / iptal etme eylemleri, bir çeviri paket anahtarı veya daha da kötüsü, bir oluşturma işlevi (veya serileştirilemeyen başka bir şey) içeren bir işlem göndermek gibi oldukça sorunlu çözümler bulacaksınız. Bunu portallarla yapmak zorunda değilsiniz ve sadece düzenli destekleri geçebilirsiniz, çünkü DeleteConfirmationPopupsadeceDeleteButton

Sonuç

Portallar kodunuzu basitleştirmek için çok kullanışlıdır. Artık onlarsız yapamazdım.

Portal uygulamalarının aşağıdaki gibi diğer yararlı özelliklerde de size yardımcı olabileceğini unutmayın:

  • Ulaşılabilirlik
  • Portalı kapatmak için Espace kısayolları
  • Dış tıklamayı işleme (portalı kapat veya kapat)
  • Bağlantı tıklamasını işleme (portalı kapat veya kapat)
  • Portal ağacında kullanıma sunulan React Context

tepki-portalı veya tepki-modal , genellikle ekranın ortasında ortalanmış olan tam ekran olması gereken pop-up'lar, modüller ve yer paylaşımları için iyidir.

tepki-tether çoğu React geliştiricisi tarafından bilinmemektedir, ancak orada bulabileceğiniz en yararlı araçlardan biridir. Tether , portal oluşturmanıza izin verir, ancak belirli bir hedefe göre portalı otomatik olarak konumlandırır. Bu, ipuçları, açılan noktalar, sıcak noktalar, yardım kutuları için mükemmel ... Konum absolute/ relativeve / z-indexveya açılır pencerenizle ilgili herhangi bir sorun yaşadıysanız , Tether sizin için tüm bunları çözecektir.

Örneğin, bir kez tıklandığında bir araç ipucuna genişleyen yerleşik hotspot'ları kolayca uygulayabilirsiniz:

Yeni etkin nokta

Gerçek üretim kodu burada. Daha basit olamaz :)

<MenuHotspots.contacts>
  <ContactButton/>
</MenuHotspots.contacts>

Düzenleme : Sadece seçtiğiniz düğüme portallar oluşturmanıza izin veren tepki tepkisini keşfetti (mutlaka vücut değil)

Düzenleme : tepki-popper tepki-tether için iyi bir alternatif olabilir gibi görünüyor . PopperJS , Tether doğrudan gövdeye eklenirken, kullanıcının doğrudan DOM'ya dokunmadan bir öğeye uygun bir konumu hesaplayan, kullanıcının DOM düğümünü nereye ve ne zaman koymak istediğini seçmesine izin veren bir kütüphanedir.

Düzenleme : Ayrıca ilginç olan ve ağacınızda istediğiniz herhangi bir yere koyduğunuz ayrılmış bir öğe yuvasına bir öğe oluşturmaya izin vererek benzer sorunları çözmenize yardımcı olabilecek tepki yuvası dolgusu var


Örnek snippet'inde, eylemi onaylarsanız onay açılır
penceresi kapanmaz (

Portal içe aktarma işleminizi kod snippet'ine dahil etmek faydalı olacaktır. Hangi kütüphaneden <Portal>geliyor? Tepki portalı sanırım, ama kesin olarak bilmek güzel olurdu.
taş

1
@ skypecakes lütfen uygulamalarımı sahte kod olarak kabul et. Hiçbir somut kütüphaneye karşı test etmedim. Burada sadece somut bir uygulama değil, kavramı öğretmeye çalışıyorum. Tepki portalı için alışkınım ve yukarıdaki kod onunla iyi çalışmalı, ancak neredeyse herhangi bir benzer lib ile iyi çalışmalıdır.
Sebastien Lorber

tepki ağ geçidi harika! Sunucu tarafı oluşturmayı destekler :)
cyrilluce

Oldukça yeni başlıyorum, bu nedenle bu yaklaşımla ilgili bazı açıklamalar için çok mutlu olacağım. Kipi gerçekten başka bir yerde oluştursanız bile, bu yaklaşımda kipin özel örneğini oluşturmanız gerekiyorsa her silme düğmesini kontrol etmeniz gerekir. Redux yaklaşımında, gösterilen veya gösterilmeyen modun yalnızca bir örneğine sahibim. Bir performans sorunu değil mi?
Amit Neuhaus

9

Konuyla ilgili JS topluluğundan bilinen uzmanların birçok iyi çözümü ve değerli yorumları burada bulunabilir. Göründüğü kadar önemsiz bir sorun olmadığını gösteren bir gösterge olabilir. Bu yüzden bu konuda şüphelerin ve belirsizliğin kaynağı olabilir.

Buradaki temel sorun, React'te yalnızca üst öğeye bileşen monte etmenize izin verilmesidir, bu da her zaman istenen davranış değildir. Ancak bu sorunu nasıl çözebiliriz?

Bu sorunu çözmek için çözüm önerisinde bulunuyorum. Daha ayrıntılı sorun tanımı, src ve örnekler burada bulunabilir: https://github.com/fckt/react-layer-stack#rationale

gerekçe

react/ react-dom, 2 temel varsayım / fikirle gelir:

  • her kullanıcı arayüzü doğal olarak hiyerarşiktir. Bu yüzden birbirimizi componentshangi şekilde sardığımız fikrine sahibiz
  • react-dom alt bileşeni (fiziksel olarak) varsayılan olarak üst DOM düğümüne bağlar

Sorun, bazen ikinci mülkün sizin durumunuzda olmasını istediğiniz şey olmamasıdır. Bazen bileşeninizi farklı fiziksel DOM düğümüne bağlamak ve aynı anda ebeveyn ve çocuk arasında mantıksal bağlantı tutmak istersiniz.

Kanonik örnek Araç İpucu benzeri bir bileşendir: geliştirme sürecinin bir noktasında sizin için bir açıklama eklemeniz gerektiğini görebilirsiniz UI element: sabit katmanda oluşturulur ve koordinatlarını (bu koordinat UI elementveya fare koordinatlarıdır ) ve aynı zamanda, şu anda gösterilip gösterilmemesi gerekip gerekmediği, içeriği ve ana bileşenlerden bazı içerikler gibi bilgilere ihtiyaç duyar. Bu örnek, bazen mantıksal hiyerarşinin fiziksel DOM hiyerarşisiyle eşleşmediğini gösterir.

Sorunuza cevap veren somut örneği görmek için https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example adresine göz atın :

import { Layer, LayerContext } from 'react-layer-stack'
// ... for each `object` in array of `objects`
  const modalId = 'DeleteObjectConfirmation' + objects[rowIndex].id
  return (
    <Cell {...props}>
        // the layer definition. The content will show up in the LayerStackMountPoint when `show(modalId)` be fired in LayerContext
        <Layer use={[objects[rowIndex], rowIndex]} id={modalId}> {({
            hideMe, // alias for `hide(modalId)`
            index } // useful to know to set zIndex, for example
            , e) => // access to the arguments (click event data in this example)
          <Modal onClick={ hideMe } zIndex={(index + 1) * 1000}>
            <ConfirmationDialog
              title={ 'Delete' }
              message={ "You're about to delete to " + '"' + objects[rowIndex].name + '"' }
              confirmButton={ <Button type="primary">DELETE</Button> }
              onConfirm={ this.handleDeleteObject.bind(this, objects[rowIndex].name, hideMe) } // hide after confirmation
              close={ hideMe } />
          </Modal> }
        </Layer>

        // this is the toggle for Layer with `id === modalId` can be defined everywhere in the components tree
        <LayerContext id={ modalId }> {({showMe}) => // showMe is alias for `show(modalId)`
          <div style={styles.iconOverlay} onClick={ (e) => showMe(e) }> // additional arguments can be passed (like event)
            <Icon type="trash" />
          </div> }
        </LayerContext>
    </Cell>)
// ...

2

Kanımca, asgari asgari uygulamanın iki şartı vardır. Kipin açık olup olmadığını takip eden bir durum ve kipin standart reaksiyon ağacının dışında yapılması için bir portal.

Aşağıdaki ModalContainer bileşeni, modal açmak için geri aramanın yürütülmesinden sorumlu olan mod ve tetik için karşılık gelen oluşturma işlevleriyle birlikte bu gereksinimleri uygular.

import React from 'react';
import PropTypes from 'prop-types';
import Portal from 'react-portal';

class ModalContainer extends React.Component {
  state = {
    isOpen: false,
  };

  openModal = () => {
    this.setState(() => ({ isOpen: true }));
  }

  closeModal = () => {
    this.setState(() => ({ isOpen: false }));
  }

  renderModal() {
    return (
      this.props.renderModal({
        isOpen: this.state.isOpen,
        closeModal: this.closeModal,
      })
    );
  }

  renderTrigger() {
     return (
       this.props.renderTrigger({
         openModal: this.openModal
       })
     )
  }

  render() {
    return (
      <React.Fragment>
        <Portal>
          {this.renderModal()}
        </Portal>
        {this.renderTrigger()}
      </React.Fragment>
    );
  }
}

ModalContainer.propTypes = {
  renderModal: PropTypes.func.isRequired,
  renderTrigger: PropTypes.func.isRequired,
};

export default ModalContainer;

Ve işte basit bir kullanım örneği ...

import React from 'react';
import Modal from 'react-modal';
import Fade from 'components/Animations/Fade';
import ModalContainer from 'components/ModalContainer';

const SimpleModal = ({ isOpen, closeModal }) => (
  <Fade visible={isOpen}> // example use case with animation components
    <Modal>
      <Button onClick={closeModal}>
        close modal
      </Button>
    </Modal>
  </Fade>
);

const SimpleModalButton = ({ openModal }) => (
  <button onClick={openModal}>
    open modal
  </button>
);

const SimpleButtonWithModal = () => (
   <ModalContainer
     renderModal={props => <SimpleModal {...props} />}
     renderTrigger={props => <SimpleModalButton {...props} />}
   />
);

export default SimpleButtonWithModal;

Oluşturma işlevlerini kullanıyorum, çünkü durum yönetimini ve kaynak plakası mantığını işlenen mod ve tetikleyici bileşeninin uygulanmasından ayırmak istiyorum. Bu, oluşturulan bileşenlerin olmasını istediğiniz her şey olmasını sağlar. Sizin durumunuzda, modal bileşenin, eşzamansız bir eylem gönderen bir geri çağrı işlevi alan bağlı bir bileşen olabileceğini düşünüyorum.

Tetikleyici bileşenden mod bileşenine dinamik sahne göndermeniz gerekiyorsa, umarım çok sık gerçekleşmez, ModalContainer'ı dinamik sahne yönetimini kendi durumunda yöneten ve yani.

import React from 'react'
import partialRight from 'lodash/partialRight';
import ModalContainer from 'components/ModalContainer';

class ErrorModalContainer extends React.Component {
  state = { message: '' }

  onError = (message, callback) => {
    this.setState(
      () => ({ message }),
      () => callback && callback()
    );
  }

  renderModal = (props) => (
    this.props.renderModal({
       ...props,
       message: this.state.message,
    })
  )

  renderTrigger = (props) => (
    this.props.renderTrigger({
      openModal: partialRight(this.onError, props.openModal)
    })
  )

  render() {
    return (
      <ModalContainer
        renderModal={this.renderModal}
        renderTrigger={this.renderTrigger}
      />
    )
  }
}

ErrorModalContainer.propTypes = (
  ModalContainer.propTypes
);

export default ErrorModalContainer;

0

Modalı bağlı bir kaba sarın ve burada asenkron işlemi gerçekleştirin. Bu şekilde eylemleri tetiklemek için gönderime ve onClose desteğine de ulaşabilirsiniz. Ulaşmak için dispatchsahne dışında, do not geçmesi mapDispatchToPropsiçin işlev connect.

class ModalContainer extends React.Component {
  handleDelete = () => {
    const { dispatch, onClose } = this.props;
    dispatch({type: 'DELETE_POST'});

    someAsyncOperation().then(() => {
      dispatch({type: 'DELETE_POST_SUCCESS'});
      onClose();
    })
  }

  render() {
    const { onClose } = this.props;
    return <Modal onClose={onClose} onSubmit={this.handleDelete} />
  }
}

export default connect(/* no map dispatch to props here! */)(ModalContainer);

Kipin işlendiği ve görünürlük durumunun ayarlandığı Uygulama:

class App extends React.Component {
  state = {
    isModalOpen: false
  }

  handleModalClose = () => this.setState({ isModalOpen: false });

  ...

  render(){
    return (
      ...
      <ModalContainer onClose={this.handleModalClose} />  
      ...
    )
  }

}
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.