极简用法的RN弹窗-支持多层弹窗

2024-01-08  本文已影响0人  wustzhy

这篇,解决了上一篇超好用的react-native 弹窗容器遗留的2个痛点


Q: 代码调用一个弹窗的极简方式是怎样的?

A: 这样子👉🏻 XxModuleAlert.show({..})

为了这个极简用法的目标,我曾经 使用rn-global-modal封装过TextInputAlert,但依然有些问题或难用点,比如遮挡了Toast、更改mask颜色要改源码、布局上有些非正常的地方

因为不希望弹窗遮挡Toast,于是研究了react-native-root-toast,用的"react-native-root-siblings",从而找到了封装理想弹窗的更快的方式

于是,基于"react-native-root-siblings": "5.0.1",,造了一个 AlertManager

Why need AlertManager

相信大家使用过RN官方Modal的,都知道,iOS弹窗无法弹两个,使用起来不满足高内聚低耦合,难受。。。

RN官方Modal

rn-global-modal

What AlertManager provide

优势

How to use AlertManager

XxModuleAlert.show({...});
XxModuleAlert.hide();
class XxModuleAlert {
  static show() {
    AlertManager.show(<XxModuleView />, {
      key: XxModuleView.name,
    });
  }
  static hide() {
    AlertManager.hide(XxModuleView.name);
  }
}

export XxModuleAlert

const XxModuleView = () => {
    ...
    return (...)
}

源码

import React from "react";
import { Dimensions, TouchableOpacity, View } from "react-native";
import RootSiblingsManager from "react-native-root-siblings";

const { width, height } = Dimensions.get("window");

/**
 * 弹窗管理类
 * - 支持同时弹出多个弹窗 (Modal、rn-global-modal 不支持)
 * - 不会遮挡Toast(Modal、rn-global-modal 会遮挡Toast)
 */
class AlertManger {
  static _alertNodes = {}; // { key1: { rootNode: <A/>, onClose: () => {} }, key2: { rootNode: <B/>, onClose: () => {} }

  /**
   * 展示弹窗
   * @param {*} modalView             弹窗内所要展示的内容组件
   * @param {*} options.key                **必传** 弹窗唯一标识
   * @param {*} options.maskStyle          蒙层样式 默认值:{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }
   * @param {*} options.maskTouchClosable  弹窗蒙层是否可点击关闭
   * @param {*} options.closedCallback     弹窗关闭的回调
   * @param {*} options.animationType      [todo] 弹窗动画类型: none | fade | slide-up
   */
  static show(modalView, options) {
    const { key, maskTouchClosable, closedCallback, maskStyle } = options;

    let rootNode;
    const onClose = () => {
      rootNode?.destroy();
      rootNode = null;
      closedCallback?.();
    };
    rootNode = new RootSiblingsManager(
      (
        <View
          style={[
            {
              position: "absolute",
              width: width,
              height: height,
              justifyContent: "center",
              alignItems: "center",
              backgroundColor: "rgba(0,0,0,0.5)",
            },
            maskStyle,
          ]}
        >
          <TouchableOpacity
            style={{
              position: "absolute",
              width: width,
              height: height,
            }}
            activeOpacity={1}
            onPress={(e) => {
              maskTouchClosable && this.hide(key);
            }}
          />
          {modalView}
        </View>
      )
    );
    this._alertNodes[key] = { rootNode, onClose };

    console.log("this._alertNodes:::", this._alertNodes);
  }

  static hide(key) {
    console.log(
      "this._alertNodes?.[key]?.onClose::",
      this._alertNodes?.[key]?.onClose
    );
    this._alertNodes?.[key]?.onClose?.();
    delete this._alertNodes[key];
  }
}

export default AlertManger;

/* ----------------------- Usage ---------------------- */
/*
 * 弹窗开发者 这样封装自己的弹窗
    ```
    import AlertManager from "app/components/dialog/AlertManager.js";
    class BbModuleAlert {
      static show() {
        AlertManager.show(<BModuleView />, {
          key: BModuleView.name,
          maskTouchClosable: true,
          closedCallback: () => {
            console.log("BModuleView closedCallback");
          },
        });
      }
      static hide() {
        AlertManager.hide(BModuleView.name);
      }
    }

    export default BbModuleAlert;

    const BModuleView = () => {
      return (
        ...
      )
    }
    ```
*/

/*
 * 弹窗调用者 这样调用弹窗
    ```
    import BbModuleAlert from "./BbModuleAlert";
    BbModuleAlert.show();
    BbModuleAlert.hide();
    ```
*/

todo

需求
某个弹窗,是当前A页面的子视图,若点击该弹窗种的按钮,跳转到下一个B页面时,该弹窗继续展示在A页面,B页面在最上层展示(是盖住该弹窗的)

上一篇 下一篇

猜你喜欢

热点阅读