Я создаю приложение, которое должно показывать диалог подтверждения в некоторых ситуациях.
Допустим, я хочу что-то удалить, затем я отправлю действие, подобное тому, deleteSomething(id)
чтобы какой-то редуктор перехватил это событие и заполнил редуктор диалога, чтобы показать его.
Мое сомнение приходит, когда этот диалог подчиняется.
- Как этот компонент может отправлять правильное действие в соответствии с первым отправленным действием?
- Должен ли создатель действия справиться с этой логикой?
- Можем ли мы добавить действия внутри редуктора?
редактировать:
чтобы было понятнее:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
Поэтому я пытаюсь повторно использовать компонент диалога. Показ / скрытие диалога не является проблемой, так как это легко сделать в редукторе. Я пытаюсь указать, как отправить действие с правой стороны в соответствии с действием, запускающим поток с левой стороны.
javascript
modal-dialog
redux
react-redux
carlesba
источник
источник
Ответы:
Подход, который я предлагаю, немного многословен, но я нашел его достаточно хорошо масштабируемым для сложных приложений. Если вы хотите показать модальный режим, запустите действие, описывающее, какой модальный режим вы хотели бы видеть:
Отправка действия, чтобы показать модальное
(Конечно, строки могут быть константами; для простоты я использую встроенные строки.)
Написание редуктора для управления модальным состоянием
Затем убедитесь, что у вас есть редуктор, который просто принимает эти значения:
Большой! Теперь, когда вы отправляете действие,
state.modal
произойдет обновление, включающее информацию о видимом в данный момент модальном окне.Написание корневого модального компонента
В корне иерархии компонентов добавьте
<ModalRoot>
компонент, подключенный к хранилищу Redux. Он будет слушатьstate.modal
и отображать соответствующий модальный компонент, перенаправляя реквизиты изstate.modal.modalProps
.Что мы здесь сделали?
ModalRoot
читает текущийmodalType
иmodalProps
изstate.modal
которого он подключен, и отображает соответствующий компонент, такой какDeletePostModal
илиConfirmLogoutModal
. Каждый модал является компонентом!Написание конкретных модальных компонентов
Здесь нет общих правил. Это просто компоненты React, которые могут отправлять действия, читать что-то из состояния хранилища и просто оказываться модальными .
Например,
DeletePostModal
может выглядеть так:Он
DeletePostModal
подключен к хранилищу, поэтому может отображать заголовок сообщения и работает как любой подключенный компонент: он может отправлять действия, в том числе,hideModal
когда необходимо скрыть себя.Извлечение презентационного компонента
Было бы неудобно копировать и вставлять одну и ту же логику компоновки для каждого «конкретного» модала. Но у вас есть компоненты, верно? Таким образом, вы можете извлечь презентационный
<Modal>
компонент, который не знает, что делают конкретные моды, но управляет тем, как они выглядят.Затем конкретные модалы, такие как
DeletePostModal
могут использовать его для рендеринга:Вы должны придумать набор реквизитов, которые
<Modal>
могут быть приняты в вашем приложении, но я думаю, что у вас может быть несколько видов модалов (например, информационный модал, подтверждающий модал и т. Д.) И несколько стилей для них.Доступность и скрытие при нажатии кнопки снаружи или клавиши Escape
Последняя важная часть о модалах заключается в том, что обычно мы хотим скрыть их, когда пользователь щелкает снаружи или нажимает Escape.
Вместо того, чтобы давать вам советы по реализации этого, я предлагаю вам просто не реализовывать это самостоятельно. Трудно получить право, учитывая доступность.
Вместо этого я бы предложил вам использовать доступный стандартный модальный компонент, такой как
react-modal
. Он полностью настраиваемый, вы можете поместить в него все, что захотите, но он правильно обрабатывает доступность, так что слепые люди все еще могут использовать ваш модал.Вы можете даже обернуть
react-modal
свой собственный,<Modal>
который принимает реквизиты, специфичные для ваших приложений и генерирует дочерние кнопки или другой контент. Это все просто компоненты!Другие подходы
Существует несколько способов сделать это.
Некоторым людям не нравится многословность этого подхода, и они предпочитают иметь
<Modal>
компонент, который они могут визуализировать прямо внутри своих компонентов с помощью техники, называемой «порталы». Порталы позволяют вам визуализировать компонент внутри вашего, в то время как фактически он будет отображаться в предопределенном месте в DOM, что очень удобно для модальных объектов.На самом деле,
react-modal
я уже ссылался на ранее сделанное внутренне, так что технически вам даже не нужно рендерить его сверху. Мне все еще приятно отделить модал, который я хочу показать, от компонента, который его показывает, но вы также можете использовать егоreact-modal
непосредственно из своих компонентов и пропустить большую часть того, что я написал выше.Я рекомендую вам рассмотреть оба подхода, поэкспериментировать с ними и выбрать то, что, по вашему мнению, лучше всего подходит для вашего приложения и для вашей команды.
источник
modalProps
действию. Это нарушает правило сохранения сериализуемого состояния. Как я могу преодолеть эту проблему?Обновление : Реагировать 16,0 введенные порталы через
ReactDOM.createPortal
ссылкуОбновление : следующие версии React (Fiber: вероятно, 16 или 17) будут включать метод для создания порталов:
ReactDOM.unstable_createPortal()
ссылкаИспользуйте порталы
Дан Абрамов ответит первая часть в порядке, но включает в себя много шаблонного. По его словам, вы также можете использовать порталы. Я немного расширю эту идею.
Преимущество портала заключается в том, что всплывающее окно и кнопка остаются очень близко к дереву React, с очень простым взаимодействием родителей и потомков с использованием реквизита: вы можете легко обрабатывать асинхронные действия с порталами или позволить родителю настроить портал.
Что такое портал?
Портал позволяет вам визуализировать непосредственно внутри
document.body
элемента, который глубоко вложен в ваше дерево React.Идея состоит в том, что, например, вы отображаете в теле следующее дерево React:
И вы получите в качестве вывода:
inside-portal
Узел был переведен внутри<body>
, вместо его обычного, глубоко вложенного места.Когда использовать портал
Портал особенно полезен для отображения элементов, которые должны идти поверх существующих компонентов React: всплывающие окна, выпадающие списки, предложения, горячие точки
Зачем использовать портал
Больше никаких проблем с z-index : портал позволяет вам выполнять рендеринг
<body>
. Если вы хотите отобразить всплывающее окно или раскрывающийся список, это действительно хорошая идея, если вы не хотите бороться с проблемами z-index. Элементы портала добавляютсяdocument.body
в порядке монтирования, что означает, что, если вы не поиграетеz-index
, по умолчанию будет складываться порталы друг над другом в порядке монтирования. На практике это означает, что вы можете безопасно открыть всплывающее окно из другого всплывающего окна и быть уверенным, что второе всплывающее окно будет отображаться поверх первого, даже не думая об этомz-index
.На практике
Самое простое: использовать локальное состояние React: если вы думаете, что для простого всплывающего окна подтверждения удаления не стоит иметь шаблон Redux, тогда вы можете использовать портал, и это значительно упрощает ваш код. Для такого случая использования, где взаимодействие является очень локальным и на самом деле довольно подробным описанием реализации, действительно ли вы заботитесь о горячей перезагрузке, перемещении во времени, регистрации действий и обо всех преимуществах, которые приносит вам Redux? Лично я не использую местное государство в этом случае. Код становится таким простым, как:
Все просто: вы все еще можете использовать состояние Redux : если вы действительно хотите, вы все равно можете использовать,
connect
чтобы выбрать, будет лиDeleteConfirmationPopup
отображаться или нет. Поскольку портал остается глубоко вложенным в ваше дерево React, очень просто настроить поведение этого портала, потому что ваш родитель может передавать реквизиты на портал. Если вы не используете порталы, вы обычно должны отображать всплывающие окна в верхней части дерева React дляz-index
причин, и обычно приходится думать о таких вещах, как «как мне настроить общий DeleteConfirmationPopup, который я построил в соответствии с вариантом использования». И обычно вы найдете довольно хакерские решения этой проблемы, такие как отправка действия, которое содержит вложенные действия подтверждения / отмены, ключ пакета перевода, или, что еще хуже, функцию рендеринга (или что-то еще, что невозможно разобрать). Вы не должны делать это с порталами, и можете просто пропустить обычные реквизиты, так какDeleteConfirmationPopup
это просто ребенокDeleteButton
Вывод
Порталы очень полезны для упрощения вашего кода. Я не мог обойтись без них больше.
Обратите внимание, что реализации портала также могут помочь вам с другими полезными функциями, такими как:
Реактивный портал или реактивный модал хороши для всплывающих окон, модальных и оверлейных программ, которые должны быть полноэкранными, обычно по центру экрана.
response-tether неизвестен большинству разработчиков React, но это один из самых полезных инструментов, которые вы можете там найти. Tether позволяет вам создавать порталы, но автоматически позиционирует портал относительно заданной цели. Это идеально подходит для всплывающих подсказок, выпадающих списков, горячих точек, справочных ящиков ... Если у вас когда-либо возникали проблемы с позицией
absolute
/relative
иz-index
вашим выпадающим списком, выходящим за пределы области просмотра, Tether решит все это за вас.Например, вы можете легко реализовать горячие точки, которые открываются во всплывающую подсказку:
Реальный производственный код здесь. Не может быть проще :)
Изменить : только что обнаруженный реагирующий шлюз, который позволяет рендерить порталы в выбранный вами узел (не обязательно тело)
Редактировать : кажется, что реакция-поппер может быть достойной альтернативой реакции-привязи. PopperJS - это библиотека, которая только вычисляет подходящую позицию для элемента, не касаясь DOM напрямую, позволяя пользователю выбирать, где и когда он хочет разместить узел DOM, в то время как Tether добавляет непосредственно к телу.
Изменить : есть также реакция-слота-заполнение, которое интересно и может помочь решить аналогичные проблемы, позволяя визуализировать элемент в зарезервированный слот элемента, который вы помещаете в любое место в своем дереве
источник
<Portal>
? Я предполагаю, что это портал реакции, но было бы неплохо знать наверняка.Здесь можно найти множество хороших решений и ценных комментариев известных экспертов из сообщества JS. Это может быть индикатором того, что это не такая тривиальная проблема, как может показаться. Я думаю, именно поэтому это может быть источником сомнений и неопределенности в этом вопросе.
Фундаментальная проблема здесь заключается в том, что в React вам разрешено монтировать компонент только к его родителю, что не всегда является желаемым поведением. Но как решить эту проблему?
Я предлагаю решение, адресованное для устранения этой проблемы. Более подробное определение проблемы, src и примеры можно найти здесь: https://github.com/fckt/react-layer-stack#rationale
Взгляните на https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example, чтобы увидеть конкретный пример, который является ответом на ваш вопрос:
источник
На мой взгляд, минимальная реализация имеет два требования. Состояние, которое отслеживает, открыт модал или нет, и портал для визуализации модального окна вне стандартного дерева реакции.
Компонент ModalContainer, приведенный ниже, реализует эти требования вместе с соответствующими функциями рендеринга для модального объекта и триггера, который отвечает за выполнение обратного вызова для открытия модального режима.
А вот простой пример использования ...
Я использую функции рендеринга, потому что я хочу изолировать управление состоянием и шаблонную логику от реализации визуализированного модального и триггерного компонента. Это позволяет отображаемым компонентам быть такими, какими вы хотите их видеть. В вашем случае, я предполагаю, что модальный компонент может быть связанным компонентом, который получает функцию обратного вызова, которая отправляет асинхронное действие.
Если вам нужно отправить динамический реквизит в модальный компонент из компонента триггера, что, как мы надеемся, происходит не слишком часто, я рекомендую обернуть ModalContainer компонентом-контейнером, который управляет динамическим реквизитом в его собственном состоянии, и улучшить исходные методы рендеринга, такие как так.
источник
Оберните модал в подключенный контейнер и выполните здесь асинхронную операцию. Таким образом, вы можете связаться как с диспетчером для запуска действий, так и с onClose. Чтобы добраться
dispatch
от реквизита, не передавайтеmapDispatchToProps
функциюconnect
.Приложение, в котором отображается модал и устанавливается его состояние видимости:
источник