Ok
отменить
...
Все думают, есть проблема с написанием таким образом, вы пишете функцию для каждого компонента?Если компонент Icon, мне нужно написать еще одинfui-icon
, решение состоит в том, чтобы положить前缀
Когда такой параметр, как:
function scopedClass(name ?: string) {
return ['fui-dialog', name].filter(Boolean).join('-')
}
Метод вызова следующий:
className={scopedClass('fui-dialog', 'mask')}
Таким образом, лучше написать стиль напрямую, это равносильно написанию метода напрасно, так что же мне делать? Это требуетФункции высшего порядка появился. Реализация выглядит следующим образом:
function scopeClassMaker(prefix: string) {
return function (name ?: string) {
return [prefix, name].filter(Boolean).join('-')
}
}
const scopedClass = scopeClassMaker('fui-dialog')
scopeClassMaker
Функция представляет собой функцию высокого уровня, которая возвращаетprefix
функция параметров.
обработка событий
Прежде чем писать обработчики событий, наш диалог должен получитьbuttons
свойство, то есть отображаемую кнопку действия, и добавить событие:
// dialog/dialog.example.tsx
...
<Dialog visible={x} buttons = {
[
<button onClick={()=> {setX(false)}}>1</button>,
<button onClick={()=> {setX(false)}}>2</button>,
]
}>
<div>hi</div>
</Dialog>
...
Когда мы это видим, первой реакцией должно быть то, что очень хлопотно так писать, я сам пишу диалог, видимый сам, кнопки и даже события сам.Пожалуйста, примите эту настройку . Хотя это хлопотно, это очень легко понять. Это не то же самое, что концепция Vue. Конечно, в будущем будут дальнейшие оптимизации.
Рендеринг внутри компонента выглядит следующим образом:
<footer className={sc('footer')}>
{
props.buttons
}
</footer>
Когда вы запустите его, вы найдете предупреждение:
В основном означает, что когда мы визуализируем массив, нам нужно добавитьkey
, Есть два решения, то есть не использовать метод массива, конечно, это не вылечит первопричину, поэтому здесьReact.cloneElemen
Когда он выйдет, он может клонировать элемент и добавить соответствующее значение атрибута следующим образом:
{
props.buttons.map((button, index) => {
React.cloneElement(button, {key: index})
})
}
Соответствующее событие click-to-close относительно простое, я не буду говорить об этом здесь, вы можете проверить это сами.исходный код .
Затем посмотрите на стиль задачи, сначала укажите стиль нашей маски:
.fui-dialog {
position: fixed; background: white; min-width: 20em;
z-index: 2;
border-radius: 4px; top: 50%; left: 50%; transform: translate(-50%, -50%);
&-mask { position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: fade_out(black, 0.5);
z-index: 1;
}
.... 以下省略其它样式
}
мы маскируем.fui-dialog-mask
использоватьfixed
Чувство позиционирования не проблема, тогда, если вы вызываете диалог на том же уровне, добавьте следующие элементы:
<div style={{position:'relative', zIndex: 10, background:'#fff'}}>666</div>
<button onClick={() => {setX(!x)}}>点击</button>
<Dialog visible={x}>
...
</Dialog>
текущий результат:
Выяснено, что маска не закрывает содержимое 666. почему это?
Это также легко понять, посмотрев на структуру, элемент маски того же уровня, что и 666, и уровень ниже, чем у 666. Конечно, его нельзя прикрыть. Тогда мы могли бы сделать это, дать.fui-dialog-mask
установить одинzIndex
больше, чем это, как9999
.
Эффект:
Что ж, я не вижу проблем.В настоящее время мы вкладываем слой zIndex в компонент Dialog.9
, Такие как:
<div style={{position:'relative', zIndex: 9, background:'#fff'}}>
<Dialog visible={x}>
...
</Dialog>
</div>
Эффект операции следующий:
Обнаружил, что родительский элемент сдерживается, и как у элементов внутри высокое значение zIndex, безрезультатно.
Так как же его сломать? Ответ: не позволяйте ему появляться внутри какого-либо элемента, как это возможно. Здесь нам необходимо ввестиВолшебный API . Этот API называетсяпортал .
Использование заключается в следующем:
return ReactDOM.createPortal(
this.props.children,
domNode
);
Первый параметр — это ваш div, а второй — куда вы хотите перейти.
import React, {Fragment, ReactElement} from 'react'
import ReactDOM from 'react-dom'
import './dialog.scss';
import {Icon} from '../index'
import {scopedClassMaker} from '../classes'
interface Props {
visible: boolean,
buttons: Array<ReactElement>,
onClose: React.MouseEventHandler,
closeOnClickMask?: boolean
}
const scopedClass = scopedClassMaker('fui-dialog')
const sc = scopedClass
const Dialog: React.FunctionComponent<Props> = (props) => {
const onClickClose: React.MouseEventHandler = (e) => {
props.onClose(e)
}
const onClickMask: React.MouseEventHandler = (e) => {
if (props.closeOnClickMask) {
props.onClose(e)
}
}
const x = props.visible ?
<Fragment>
<div className={sc('mask')} onClick={onClickMask}>
</div>
<div className={sc()}>
<div className={sc('close')} onClick={onClickClose}>
<Icon name='close'/>
</div>
<header className={sc('header')}>提示</header>
<main className={sc('main')}>
{props.children}
</main>
<footer className={sc('footer')}>
{
props.buttons.map((button, index) => {
React.cloneElement(button, {key: index})
})
}
</footer>
</div>
</Fragment>
:
null
return (
ReactDOM.createPortal(x, document.body)
)
}
Dialog.defaultProps = {
closeOnClickMask: false
}
export default Dialog
текущий результат:
Конечно, если уровень Dialog меньше, чем zIndex того же уровня, он все равно не может быть покрыт. ЭтоzIndex
Как правило, установить, насколько это разумнее. в общемDialog Этот слой настроен на1
, слой маски установлен на2
. Чем меньше параметр, тем лучше, потому что пользователь может его изменить.
Управление zIndex
Управлением zIndex обычно занимаются фронтенд-архитекторы, оно разделено по бизнес-сценариям, например, реклама должна быть вверху страницы, поэтому zIndex, как правило, самый продвинутый.
Оповещение с удобным API
То, как мы используем компонент Dialog для вызова вышеуказанного, более проблематично, и мы написали много.Иногда мы думаем об использовании предупреждения для прямого всплывающего диалогового окна.Это так просто и удобно. Такие как
<h1>example 3</h1>
<button onClick={() => alert('1')}>alert</button>
Мы хотим щелкнуть кнопку напрямую, а затем открыть наш настраиваемый диалог с содержимым 1, который необходимо экспортировать в компонент «Диалог».alert
Методы, как показано ниже:
// dialog/dialog.tsx
...
const alert = (content: string) => {
const component = <Dialog visible={true} onClose={() => {}}>
{content}
</Dialog>
const div = document.createElement('div')
document.body.append(div)
ReactDOM.render(component, div)
}
export {alert}
...
текущий результат:
Но есть проблема, потому что видимость диалога передается извне, а React — это односторонний поток данных, и видимость нельзя изменить напрямую в компоненте, поэтому в методе onClose нам нужно отрендерить новый компонент снова и установить новые компонентыvisible
дляture
, перезапишите исходный компонент:
...
const alert = (content: string) => {
const component = <Dialog visible={true} onClose={() => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}}>
{content}
</Dialog>
const div = document.createElement('div')
document.body.append(div)
ReactDOM.render(component, div)
}
..
Удобный API для подтверждения
подтвердить метод вызова:
<button onClick={() => confirm('1', ()=>{}, ()=> {})}>confirm</button>
Первый параметр — отображаемое содержимое, второй параметр — обратный вызов подтверждения, а третий параметр — функция обратного вызова отмены.
Метод реализации:
const confirm = (content: string, yes?: () => void, no?: () => void) => {
const onYes = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
yes && yes()
}
const onNo = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
no && no()
}
const component = (
<Dialog
visible={true} onClose={() => { onNo()}}
buttons={[<button onClick={onYes}>yes</button>,
<button onClick={onNo}>no</button>
]}
>
{content}
</Dialog>)
const div = document.createElement('div')
document.body.appendChild(div)
ReactDOM.render(component, div)
}
Обработка событий аналогична Alter, единственным дополнительным шагом являетсяconfirm
при нажатииyes
илиno
При внешнем обратном вызове необходимо вызвать соответствующую функцию обратного вызова.
Удобный модальный API
метод модального вызова:
<button onClick={() => {modal(<h1>你好</h1>)}}>modal</button>
Содержимое, передаваемое модальным окном, — это не просто текст, а элементы.
Метод реализации:
const modal = (content: ReactNode | ReactFragment) => {
const onClose = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}
const component = <Dialog onClose={onClose} visible={true}>
{content}
</Dialog>
const div = document.createElement('div')
document.body.appendChild(div)
ReactDOM.render(component, div)
}
Обратите внимание, тип контента здесь.
текущий результат:
Есть еще одна проблема, если вам нужно добавить кнопку, вы можете написать это так:
<button onClick={() => {modal(<h1>
你好 <button>close</button></h1>
)}}>modal</button>
Его нельзя закрыть, потому чтоDialog упакован вmodal
внутри. Если вы хотите закрыть, вы должны контролироватьvisible
, то, очевидно, я не могу управлять изнутри извнеvisible
, так что этоbutton
нет способа поставить этоmodal
выключенный.
Решение состоит в использовании замыкания, мы можем вернуть метод close в модальном методе:
const modal = (content: ReactNode | ReactFragment) => {
const onClose = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
}
const component = <Dialog onClose={onClose} visible={true}>
{content}
</Dialog>
const div = document.createElement('div')
document.body.appendChild(div)
ReactDOM.render(component, div)
return onClose;
}
Наконец, есть перезапуск onClose.Благодаря функции замыкания метод onClose, возвращаемый внешним вызовом, может обращаться к внутренним переменным.
Метод вызова:
const openModal = () => {
const close = modal(<h1>你好
<button onClick={() => close()}>close</button>
</h1>)
}
<button onClick={openModal}>modal</button>
Рефакторинг API
Перед рефакторингом нам нужно абстрагировать соответствующие методы в alert, confirm, modal:
Как видно из таблицы, модальный и два других имеют только еще один rerun api, на самом деле два других тоже могут возвращать соответствующий Api, но мы его не вызывали, так что добавляем:
Таким образом, три функции похожи на абстрактном уровне, поэтому три функции следует объединить в одну.
Сначала извлеките общедоступную часть и назовите ее первой.x
, содержание следующее:
const x= (content: ReactNode, buttons ?:Array<ReactElement>, afterClose?: () => void) => {
const close = () => {
ReactDOM.render(React.cloneElement(component, {visible: false}), div)
ReactDOM.unmountComponentAtNode(div)
div.remove()
afterClose && afterClose()
}
const component =
<Dialog visible={true}
onClose={() => {
close(); afterClose && afterClose()
}}
buttons={buttons}
>
{content}
</Dialog>
const div = document.createElement('div')
document.body.append(div)
ReactDOM.render(component, div)
return close
}
Переработанный код оповещения выглядит следующим образом:
const alert = (content: string) => {
const button = <button onClick={() => close()}>ok</button>
const close = x(content, [button])
}
Переработанный код подтверждения выглядит следующим образом:
const confirm = (content: string, yes?: () => void, no?: () => void) => {
const onYes = () => {
close()
yes && yes()
}
const onNo = () => {
close()
no && no()
}
const buttons = [
<button onClick={onYes}>yes</button>,
<button onClick={onNo}>no</button>
]
const close = modal(content, buttons, no)
}
Модальная реконструкция следующего кода:
const modal = (content: ReactNode | ReactFragment) => {
return x(content)
}
Наконец нашел, чтоx
Путь этоmodal
метод, поэтому изменитеx
названныйmodal
, удалить соответствующийmodal
определение.
Суммировать
Использование функций более высокого порядка scopedClass
портал портал
Динамически генерировать компоненты
API закрытия прохода
Этот компонент использует оптимизированный стиль. Если вам интересно, вы можете оптимизировать его самостоятельно. Исходный код этого раздела выложен наздесь серединаlib/dialog
.
Ссылаться на
Курс React Wheel Building учителя Фан Инхана
общаться с
Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884… Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние , вы можете увидеть преимущества, вы знаете.