मैं Redux में एक मोडल संवाद कैसे प्रदर्शित कर सकता हूं जो अतुल्यकालिक क्रियाएं करता है?


240

मैं एक ऐप बना रहा हूं, जिसे कुछ स्थितियों में एक पुष्टि संवाद दिखाने की आवश्यकता है।

मान लीजिए कि मैं कुछ निकालना चाहता हूं, तो मैं एक एक्शन deleteSomething(id)भेजूंगा जैसे कि कुछ reducer उस घटना को पकड़ लेंगे और इसे दिखाने के लिए संवाद reducer को भर देंगे।

मेरा संदेह तब होता है जब यह संवाद प्रस्तुत करता है।

  • पहली कार्रवाई के अनुसार यह घटक उचित कार्रवाई कैसे कर सकता है?
  • क्या एक्शन क्रिएटर को इस तर्क को संभालना चाहिए?
  • क्या हम reducer के अंदर क्रियाओं को जोड़ सकते हैं?

संपादित करें:

इसे स्पष्ट करने के लिए:

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

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

इसलिए मैं संवाद घटक का पुन: उपयोग करने की कोशिश कर रहा हूं। संवाद दिखाना / छिपाना यह समस्या नहीं है क्योंकि यह आसानी से reducer में किया जा सकता है। मैं जो निर्दिष्ट करने की कोशिश कर रहा हूं वह यह है कि बाईं ओर प्रवाह शुरू करने वाली क्रिया के अनुसार दाईं ओर से कार्रवाई को कैसे निकालना है।


1
मुझे लगता है कि आपके मामले में संवाद की स्थिति (छिपाने / दिखाने) स्थानीय है। मैं डायलॉग दिखाने / छिपाने के प्रबंधन के लिए प्रतिक्रिया स्थिति का उपयोग करना चुनूंगा। इस तरह, "पहली कार्रवाई के अनुसार उचित कार्रवाई" का सवाल चलेगा।
मिंग

जवाबों:


516

मेरे द्वारा सुझाया गया दृष्टिकोण थोड़ा क्रियात्मक है, लेकिन मैंने इसे जटिल ऐप्स में बहुत अच्छे पैमाने पर पाया है। आप एक मॉडल को दिखाने के लिए चाहते हैं, तो आग एक कार्रवाई का वर्णन है जो बताएं कि आप मोडल को देखना चाहते हैं:

मोडल दिखाने के लिए एक एक्शन डिस्पैच करना

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

(स्ट्रिंग्स निश्चित रूप से स्थिर हो सकते हैं; मैं सरलता के लिए इनलाइन स्ट्रिंग्स का उपयोग कर रहा हूं।)

मोडल राज्य को प्रबंधित करने के लिए एक Reducer लिखना

फिर सुनिश्चित करें कि आपके पास एक reducer है जो इन मूल्यों को स्वीकार करता है:

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 */
})

महान! अब, जब आप कोई कार्रवाई भेजते हैं, state.modalतो वर्तमान में दिखाई देने वाली मोडल विंडो के बारे में जानकारी शामिल करने के लिए अपडेट करेगा।

रूट मोडल कंपोनेंट लिखना

अपने घटक पदानुक्रम की जड़ में, एक <ModalRoot>घटक जोड़ें जो Redux स्टोर से जुड़ा है। यह state.modalएक उपयुक्त मोडल घटक को सुनेगा और प्रदर्शित करेगा , जिससे प्रॉप्स को आगे बढ़ाया जाएगा 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)

हमने यहाँ क्या किया है? ModalRootवर्तमान में पढ़ता है modalTypeऔर modalPropsसे state.modalजो करने के लिए यह जुड़ा हुआ है, और renders एक इसी घटक जैसे DeletePostModalयाConfirmLogoutModal । हर मोडल एक घटक है!

विशिष्ट मोडल घटक लिखना

यहां कोई सामान्य नियम नहीं हैं। वे बस रिएक्ट घटक हैं जो क्रियाओं को प्रेषित कर सकते हैं, स्टोर राज्य से कुछ पढ़ सकते हैं, और बस मॉडल हो सकते हैं

उदाहरण के लिए, ऐसा DeletePostModalलग सकता है:

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)

यह DeletePostModalस्टोर से जुड़ा हुआ है इसलिए यह पोस्ट शीर्षक प्रदर्शित कर सकता है और किसी भी जुड़े घटक की तरह काम करता है: यह कार्यों को भेज सकता है, जिसमें शामिल हैंhideModal स्वयं को छिपाना भी आवश्यक है।

एक प्रस्तुति घटक निकालना

हर "विशिष्ट" मोडल के लिए एक ही लेआउट लॉजिक को कॉपी-पेस्ट करना अजीब होगा। लेकिन आपके पास घटक हैं, है ना? तो आप एक प्रस्तुतिकरण निकाल सकते हैं <Modal> कंपोनेंट हैं, जो यह नहीं जानता कि विशेष तौर पर क्या करते हैं, लेकिन वे कैसे देखते हैं।

फिर, विशिष्ट मोडल जैसे कि DeletePostModalइसे प्रतिपादन के लिए उपयोग कर सकते हैं:

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)

यह आपके लिए है कि <Modal>आप अपने आवेदन में स्वीकार कर सकते हैं, लेकिन मैं कल्पना करूँगा कि आपके पास कई प्रकार के मॉडल्स हो सकते हैं (जैसे जानकारी मोडल, पुष्टि मोडल, आदि), और उनके लिए कई शैलियाँ।

क्लिक आउटसाइड या एस्केप कुंजी पर एक्सेसिबिलिटी और हाइडिंग

मॉडल के बारे में अंतिम महत्वपूर्ण हिस्सा यह है कि आम तौर पर हम उन्हें छुपाना चाहते हैं जब उपयोगकर्ता बाहर क्लिक करता है या एस्केप दबाता है।

आपको इसे लागू करने की सलाह देने के बजाय, मेरा सुझाव है कि आप इसे स्वयं लागू न करें। पहुंच पर विचार करना सही है।

इसके बजाय, मैं आपको एक सुलभ ऑफ-द-शेल्फ मोडल घटक जैसे कि उपयोग करने का सुझाव दूंगा react-modal। यह पूरी तरह से अनुकूलन योग्य है, आप इसके अंदर कुछ भी डाल सकते हैं, लेकिन यह पहुंच को सही ढंग से संभालता है ताकि अंधे लोग अभी भी आपके मॉडल का उपयोग कर सकें।

तुम भी react-modalअपने आप में लपेट कर सकते हैं <Modal>कि अपने अनुप्रयोगों के लिए विशिष्ट सहारा स्वीकार करता है और बाल बटन या अन्य सामग्री उत्पन्न करता है। यह सब सिर्फ घटक है!

अन्य दृष्टिकोण

इसे करने का एक से अधिक तरीका है।

कुछ लोग इस दृष्टिकोण की वाचालता को पसंद नहीं करते हैं और एक <Modal>घटक को पसंद करते हैं जिसे वे "पोर्टल्स" नामक तकनीक के साथ अपने घटकों के अंदर सही तरीके से प्रस्तुत कर सकते हैं । पोर्टल आपको अपने अंदर एक घटक प्रदान करते हैं, जबकि वास्तव में यह DOM में एक पूर्व निर्धारित जगह पर प्रस्तुत करेगा, जो modals के लिए बहुत सुविधाजनक है।

वास्तव में react-modalमैं पहले से जुड़ा हुआ है कि आंतरिक रूप से इतना तकनीकी रूप से आपको ऊपर से इसे प्रस्तुत करने की आवश्यकता नहीं है। मुझे अभी भी यह अच्छा लगता है कि मैं इसे दिखाने वाले घटक से दिखाना चाहता हूं, लेकिन आप react-modalअपने घटकों से सीधे उपयोग कर सकते हैं , और जो मैंने ऊपर लिखा है, उसे छोड़ दें।

मैं आपको दोनों दृष्टिकोणों पर विचार करने, उनके साथ प्रयोग करने और अपने ऐप के लिए और अपनी टीम के लिए सबसे अच्छा काम करने के लिए प्रोत्साहित करता हूं।


35
एक बात मैं सुझाता हूं कि रिड्यूसर उन मॉडल्स की एक सूची बनाए रखता है जिन्हें धक्का दिया जा सकता है और पॉप किया जा सकता है। जैसा कि मूर्खतापूर्ण लगता है, मैं लगातार उन परिस्थितियों में भाग रहा हूं जहां डिजाइनर / उत्पाद प्रकार चाहते हैं कि मैं एक मॉडल से एक मॉडल खोलूं, और उपयोगकर्ताओं को "वापस जाने" की अनुमति देना अच्छा है।
काइल

9
हाँ, निश्चित रूप से, यह उसी तरह की चीज़ है जिसे Redux बनाना आसान बनाता है क्योंकि आप अपने राज्य को केवल एक सरणी में बदल सकते हैं। व्यक्तिगत रूप से मैंने उन डिजाइनरों के साथ काम किया है, जो इसके विपरीत, चाहते थे कि मॉडल्स अनन्य हों, इसलिए मैंने जो दृष्टिकोण लिखा वह आकस्मिक घोंसले के शिकार था। लेकिन हाँ, आपके पास इसके दोनों तरीके हो सकते हैं।
दान अब्रामोव

4
अपने अनुभव में मैं कहूंगा: यदि मोडल किसी स्थानीय घटक से संबंधित है (जैसे डिलीट कन्फर्मेशन मोडल डिलीट बटन से संबंधित है), तो पोर्टल का उपयोग करना सरल है, अन्यथा Redux क्रियाओं का उपयोग करें। @ केली के साथ सहमत एक मॉडल से एक मॉडल को खोलने में सक्षम होना चाहिए। यह पोर्टल्स के साथ डिफ़ॉल्ट रूप से भी काम करता है क्योंकि वे बॉडी को दस्तावेज़ करने के लिए जोड़े जाते हैं ताकि पोर्टल्स एक-दूसरे पर अच्छी तरह से ढेर हो जाएं (जब तक कि आप z- इंडेक्स के साथ सब कुछ गड़बड़ न करें: p)
सेबस्टियन लम्बर

4
@DanAbramov, आपका समाधान बहुत अच्छा है, लेकिन मुझे मामूली समस्या है। कोई गंभीर बात नहीं। मैं प्रोजेक्ट में सामग्री-यूआई का उपयोग करता हूं, जब मोडल को बंद करने के बाद इसे "बंद" करने के बजाय "फीका" एनीमेशन को बंद कर देता हूं। संभवतः किसी प्रकार की देरी करने की आवश्यकता है? या हर मॉडल को मोडलरूट के अंदर एक सूची के रूप में रखें? सुझाव?
ग्वार

7
कभी-कभी मैं मोडल बंद होने के बाद कुछ फ़ंक्शन को कॉल करना चाहता हूं (उदाहरण के लिए, मोडल के अंदर इनपुट फ़ील्ड मानों के साथ फ़ंक्शन कॉल करें)। मैं modalPropsकार्रवाई के रूप में इन कार्यों को पारित करेंगे । यह हालांकि राज्य को क्रमबद्ध रखने के नियम का उल्लंघन करता है। मैं इस मुद्दे को कैसे दूर कर सकता हूं?
chmanie

98

अद्यतन : प्रतिक्रिया 16.0 लिंक के माध्यम से पोर्टल पेश कियाReactDOM.createPortal

अद्यतन : प्रतिक्रिया के अगले संस्करणों (फाइबर: शायद 16 या 17) में पोर्टल बनाने की एक विधि शामिल होगी: ReactDOM.unstable_createPortal() लिंक


पोर्टल का उपयोग करें

दान अब्रामोव का पहला भाग ठीक है, लेकिन इसमें बहुत सारे बॉयलरप्लेट शामिल हैं। जैसा कि उन्होंने कहा, आप पोर्टल्स का भी उपयोग कर सकते हैं। मैं उस विचार पर थोड़ा विस्तार करूँगा।

एक पोर्टल का लाभ यह है कि पॉपअप और बटन रिएक्ट ट्री में बहुत करीब रहते हैं, जिसमें बहुत ही सरल माता-पिता / बच्चे संचार का उपयोग करते हैं: आप आसानी से पोर्टल के साथ async क्रियाओं को संभाल सकते हैं, या माता-पिता को पोर्टल को अनुकूलित करने दे सकते हैं।

एक पोर्टल क्या है?

एक पोर्टल आपको सीधे document.bodyएक तत्व के अंदर रेंडर करने की अनुमति देता है जो आपके रिएक्ट ट्री में गहरा घोंसला है।

विचार यह है कि उदाहरण के लिए आप शरीर में निम्नलिखित रिएक्ट ट्री प्रस्तुत करते हैं:

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

और आपको आउटपुट के रूप में मिलता है:

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

inside-portalनोड के अंदर अनुवाद किया गया है <body>अपनी सामान्य, गहरा-नेस्टेड जगह के बजाय,।

जब एक पोर्टल का उपयोग करने के लिए

एक पोर्टल उन तत्वों को प्रदर्शित करने के लिए विशेष रूप से उपयोगी है जो आपके मौजूदा रिएक्ट घटकों के शीर्ष पर जाने चाहिए: पॉपअप, ड्रॉपडाउन, सुझाव, हॉटस्पॉट्स

एक पोर्टल का उपयोग क्यों करें

कोई z- इंडेक्स समस्याएं अब नहीं हैं : एक पोर्टल आपको रेंडर करने की अनुमति देता है <body>। यदि आप एक पॉपअप या ड्रॉपडाउन प्रदर्शित करना चाहते हैं, तो यह एक बहुत अच्छा विचार है यदि आप z- इंडेक्स समस्याओं के खिलाफ लड़ाई नहीं करना चाहते हैं। पोर्टल तत्व document.bodyमाउंट ऑर्डर में जोड़े जाते हैं , जिसका अर्थ है कि जब तक आप साथ नहीं खेलते हैं z-index, डिफ़ॉल्ट व्यवहार बढ़ते क्रम में, एक दूसरे के ऊपर पोर्टल्स को स्टैक करने के लिए होगा। व्यवहार में, इसका मतलब है कि आप एक पॉपअप को दूसरे पॉपअप के अंदर से सुरक्षित रूप से खोल सकते हैं, और सुनिश्चित करें कि 2 पॉपअप पहले के शीर्ष पर प्रदर्शित किया जाएगा, इसके बारे में भी सोचने के बिना z-index

प्रयोग में

सबसे सरल: स्थानीय प्रतिक्रिया स्थिति का उपयोग करें: यदि आपको लगता है, एक साधारण हटाने की पुष्टि पॉपअप के लिए, यह Redux बॉयलरप्लेट के लायक नहीं है, तो आप एक पोर्टल का उपयोग कर सकते हैं और यह आपके कोड को बहुत सरल करता है। ऐसे उपयोग के मामले के लिए, जहां इंटरैक्शन बहुत स्थानीय है और वास्तव में काफी कार्यान्वयन विस्तार है, क्या आप वास्तव में हॉट-रीलोडिंग, टाइम-ट्रैवलिंग, एक्शन लॉगिंग और सभी लाभों के बारे में परवाह करते हैं जो Redux आपको लाता है? व्यक्तिगत रूप से, मैं इस मामले में स्थानीय स्थिति का उपयोग नहीं करता हूं। कोड उतना ही सरल हो जाता है:

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>
    );
  }
}

सरल: आप अभी भी Redux राज्य का उपयोग कर सकते हैं : यदि आप वास्तव में चाहते हैं, तो आप अभी भी connectयह चुनने के लिए उपयोग कर सकते हैं कि DeleteConfirmationPopupदिखाया गया है या नहीं। चूंकि पोर्टल आपके रिएक्ट ट्री में गहराई से रहता है, इसलिए इस पोर्टल के व्यवहार को अनुकूलित करना बहुत सरल है क्योंकि आपके माता-पिता पोर्टल पर प्रॉपर से गुजर सकते हैं। यदि आप पोर्टल्स का उपयोग नहीं करते हैं, तो आपको आमतौर पर अपने रिएक्ट ट्री के शीर्ष पर अपने पॉपअप को प्रस्तुत करना होगाz-index कारण हैं , और आमतौर पर चीजों के बारे में सोचना होगा "मैं जेनेरिक DeleteConfirmationPopup को कैसे अनुकूलित करता हूं, मैं उपयोग के मामले के अनुसार बनाया गया हूं "। और आमतौर पर आप इस समस्या का काफी आसान समाधान पाएंगे, जैसे कि एक क्रिया को भेजना, जिसमें नेस्टेड पुष्टिकरण / रद्द करने की क्रियाएं, एक ट्रांसलेशन बंडल कुंजी, या इससे भी बदतर, एक रेंडर फ़ंक्शन (या कुछ और अनजाने में) शामिल है। आपको पोर्टल्स के साथ ऐसा करने की ज़रूरत नहीं है, और बस नियमित रूप से प्रॉपर पास कर सकते हैं, क्योंकि DeleteConfirmationPopupअभी बच्चा हैDeleteButton

निष्कर्ष

पोर्टल आपके कोड को सरल बनाने के लिए बहुत उपयोगी हैं। मैं अब उनके बिना नहीं कर सकता।

ध्यान दें कि पोर्टल कार्यान्वयन आपको अन्य उपयोगी सुविधाओं के साथ भी मदद कर सकता है:

  • सरल उपयोग
  • पोर्टल को बंद करने के लिए एस्पास शॉर्टकट
  • क्लिक के बाहर हैंडल (करीबी पोर्टल या नहीं)
  • लिंक लिंक पर क्लिक करें (करीब पोर्टल या नहीं)
  • प्रतिक्रिया संदर्भ पोर्टल पेड़ में उपलब्ध कराया गया

पॉप - अप, मोडल और ओवरले के लिए प्रतिक्रिया-पोर्टल या रिएक्शन-मोडल अच्छे हैं, जो फुल-स्क्रीन होने चाहिए, जो आमतौर पर स्क्रीन के बीच में केंद्रित होते हैं।

रिएक्ट-टीथर अधिकांश रिएक्ट डेवलपर्स के लिए अज्ञात है, फिर भी यह सबसे उपयोगी उपकरणों में से एक है जिसे आप वहां पता कर सकते हैं। टीथर आपको पोर्टल बनाने की अनुमति देता है, लेकिन स्वचालित रूप से पोर्टल को स्थिति देगा, किसी दिए गए लक्ष्य के सापेक्ष। यह टूलटिप्स, ड्रॉपडाउन, हॉटस्पॉट, हेल्पबॉक्स के लिए एकदम सही है ... यदि आपको कभी भी पोजिशन और absolute/ या आपके व्यूपोर्ट के बाहर जाने की समस्या है, तो टीथर आपके लिए वह सब हल कर देगा।relativez-index

उदाहरण के लिए, आप आसानी से ऑनबोर्डिंग हॉटस्पॉट को लागू कर सकते हैं, जो एक बार क्लिक किए जाने पर टूलटिप तक फैल जाता है:

ऑनबोर्डिंग हॉटस्पॉट

असली उत्पादन कोड यहाँ। कोई सरल नहीं हो सकता है :)

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

संपादित करें : बस खोज की गई प्रतिक्रिया-प्रवेश द्वार जो पोर्टल्स को आपकी पसंद के नोड में प्रस्तुत करने की अनुमति देता है (जरूरी नहीं कि शरीर)

संपादित करें : ऐसा लगता है कि प्रतिक्रिया-पॉपर प्रतिक्रिया-टीथर का एक सभ्य विकल्प हो सकता है। पॉपपरजेएस एक पुस्तकालय है जो केवल एक तत्व के लिए एक उपयुक्त स्थिति की गणना करता है, बिना डीओएम को सीधे स्पर्श किए बिना, उपयोगकर्ता को यह चुनने देता है कि वह डोम नोड को कहां और कब डालना चाहता है, जबकि टेडर सीधे शरीर में जोड़ता है।

संपादित करें : इसमें प्रतिक्रिया-स्लॉट-भरण भी है जो दिलचस्प है और एक तत्व को आरक्षित तत्व स्लॉट को प्रस्तुत करने की अनुमति देकर समान समस्याओं को हल करने में मदद कर सकता है जिसे आप अपने पेड़ में कहीं भी रखना चाहते हैं।


यदि आप कार्रवाई की पुष्टि करते हैं (विरोध करते समय आप
कैंसिल

कोड स्निपेट में अपने पोर्टल आयात को शामिल करना मददगार होगा। पुस्तकालय से क्या <Portal>आता है? मुझे लगता है कि यह प्रतिक्रिया-पोर्टल है, लेकिन यह सुनिश्चित करने के लिए जानना अच्छा होगा।
पत्थर

1
@skypecakes कृपया मेरे कार्यान्वयन को छद्म कोड के रूप में मानें। मैंने किसी भी ठोस पुस्तकालय के खिलाफ इसका परीक्षण नहीं किया। मैं यहां केवल अवधारणा को पढ़ाने की कोशिश करता हूं, ठोस क्रियान्वयन की नहीं। मैं प्रतिक्रिया-पोर्टल और ऊपर दिए गए कोड का उपयोग करता हूं, इसके साथ ठीक काम करना चाहिए, लेकिन यह लगभग किसी भी समान काम के साथ ठीक काम करना चाहिए।
सेबेस्टियन लॉर्बर

प्रतिक्रिया-प्रवेश द्वार कमाल है! यह सर्वर साइड रेंडरिंग का समर्थन करता है :)
cyrilluce

मैं बहुत शुरुआती हूं इसलिए इस दृष्टिकोण पर कुछ स्पष्टीकरण के लिए बहुत खुश होंगे। यहां तक ​​कि अगर आप वास्तव में किसी अन्य स्थान पर मोडल को प्रस्तुत करते हैं, तो इस दृष्टिकोण में आपको प्रत्येक डिलीट बटन पर जांचना होगा यदि आपको मोडल के विशिष्ट उदाहरण को प्रस्तुत करना चाहिए। Redux एप्रोच में मेरे पास मोडल का केवल एक उदाहरण है जो दिखाया गया है या नहीं। क्या यह प्रदर्शन की चिंता नहीं है?
अमित नेउहाउस

9

विषय पर जेएस समुदाय के ज्ञात विशेषज्ञों द्वारा कई अच्छे समाधान और मूल्यवान टिप्पणियां यहां पाई जा सकती हैं। यह एक संकेतक हो सकता है कि यह उतनी तुच्छ समस्या नहीं है जितनी यह लग सकती है। मुझे लगता है कि यह इस मुद्दे पर संदेह और अनिश्चितता का स्रोत हो सकता है।

यहां मौलिक समस्या यह है कि रिएक्ट में आपको केवल अपने माता-पिता को घटक माउंट करने की अनुमति है, जो हमेशा वांछित व्यवहार नहीं होता है। लेकिन इस मुद्दे को कैसे संबोधित किया जाए?

मैं इस मुद्दे को ठीक करने के लिए संबोधित समाधान का प्रस्ताव करता हूं। अधिक विस्तृत समस्या परिभाषा, src और उदाहरण यहां देखे जा सकते हैं: https://github.com/fckt/react-layer-st.co#rationale

दलील

react/ react-dom2 बुनियादी मान्यताओं / विचारों के साथ आता है:

  • हर यूआई स्वाभाविक रूप से पदानुक्रमित है। यही कारण है कि हमारे पास componentsएक दूसरे को लपेटने का विचार है
  • react-dom डिफ़ॉल्ट रूप से अपने मूल DOM नोड में mounts (शारीरिक रूप से) बाल घटक

समस्या यह है कि कभी-कभी दूसरी संपत्ति वह नहीं होती जो आप अपने मामले में चाहते हैं। कभी-कभी आप अपने घटक को विभिन्न भौतिक डोम नोड में माउंट करना चाहते हैं और एक ही समय में माता-पिता और बच्चे के बीच तार्किक संबंध रखते हैं।

कैनोनिकल उदाहरण टूलटिप जैसा घटक है: विकास प्रक्रिया के कुछ बिंदु पर आप पा सकते हैं कि आपको अपने लिए कुछ विवरण जोड़ने की आवश्यकता है UI element: यह निश्चित परत में प्रस्तुत करेगा और इसके निर्देशांक (जो कि समन्वय UI elementया माउस कोर्डर्स हैं) को जानना चाहिए और एक ही समय में यह जानकारी की आवश्यकता है कि क्या इसे अभी दिखाने की आवश्यकता है या नहीं, इसकी सामग्री और मूल घटकों से कुछ संदर्भ। यह उदाहरण दिखाता है कि कभी-कभी तार्किक पदानुक्रम भौतिक DOM पदानुक्रम के साथ मेल नहीं खाता है।

अपने प्रश्न के उत्तर का ठोस उदाहरण देखने के लिए https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example पर एक नज़र डालें :

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

मेरी राय में नंगे न्यूनतम कार्यान्वयन की दो आवश्यकताएं हैं। एक राज्य जो मोडल खुला है या नहीं, और मानक प्रतिक्रिया पेड़ के बाहर मॉडल को प्रस्तुत करने के लिए एक पोर्टल रखता है।

ModalContainer घटक नीचे उन आवश्यकताओं को लागू करता है जो मोडल और ट्रिगर के लिए संबंधित रेंडर फ़ंक्शन के साथ-साथ होती हैं, जो मोडल को खोलने के लिए कॉलबैक को निष्पादित करने के लिए जिम्मेदार है।

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;

और यहाँ एक सरल उपयोग मामला है ...

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;

मैं रेंडर फ़ंक्शंस का उपयोग करता हूं, क्योंकि मैं राज्य प्रबंधन और बॉयलरप्लेट लॉजिक को प्रदान किए गए मोडल और ट्रिगर घटक के कार्यान्वयन से अलग करना चाहता हूं। यह प्रदान किए गए घटकों को जो कुछ भी आप चाहते हैं उन्हें होने की अनुमति देता है। आपके मामले में, मुझे लगता है कि मोडल घटक एक जुड़ा घटक हो सकता है जो एक कॉलबैक फ़ंक्शन प्राप्त करता है जो एक एसिंक्रोनस कार्रवाई को भेजता है।

यदि आपको ट्रिगर घटक से मोडल कंपोनेंट में डायनेमिक प्रॉप्स भेजने की आवश्यकता होती है, जो कि उम्मीद के मुताबिक अक्सर नहीं होता है, तो मैं एक कंटेनर कंपोनेंट के साथ मोडलकोनटेनर को लपेटने की सलाह देता हूं जो डायनेमिक प्रॉप्स को अपने राज्य में मैनेज करता है और ऑरिजनल प्रॉसेस को बढ़ाता है जैसे इसलिए।

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

एक कनेक्टेड कंटेनर में मोडल लपेटें और यहाँ पर एसिंक्स ऑपरेशन करें। इस तरह आप क्रियाओं को चालू करने के लिए प्रेषण तक पहुँच सकते हैं और ऑन-प्रोज़ भी। dispatchप्रॉप्स से पहुंचने के लिए , फंक्शन पास mapDispatchToProps करें 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);

एप्लिकेशन जहां मोडल प्रदान किया गया है और इसकी दृश्यता स्थिति सेट की गई है:

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

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

  ...

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

}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.