手写|手写 React-Native 方法调用式的 Modal 弹框、Toast 提示

在使用 React-Native 的时候,经常看到一些第三方库的 Modal、Toast 使用方式是这样的:

/** 显示/隐藏 Modal */ Modal.show({/** 做一些快乐的事 */}); Modal.hide(); /** 显示/隐藏 Toast */ Toast.show('做一些开心的事'); Toast.hide();

那这种弹框在 RN 中是怎么实现的呢?
众所周知,在Web端项目中,实现类似的功能非常之简单:
const modal = document.createElement('div'); modal.style.position = 'fixed'; modal.innerHTML = '做一些快乐的事'; document.body.appendChild(modal);

在 RN 中没有 document 对象,元素也没有 appendChild 方法,该怎么做?
思路:
添加一个根元素的兄弟元素,其样式 “position: absolute, zIndex: 999” ,调用其静态方法使其重新 render 。
实现步骤:
  1. 创建一个 RootView 组件
/** 定义一个变量用来存储 RootView 实例 */ let rootViewInstance: RootView | undefined; class RootView extends React.Component { constructor(props: {}) { super(props); rootViewInstance = this; // 将实例赋值给 rootViewInstance }public readonly state = { content: null }; public static setContent(content) { rootViewInstance.setState({ content }); }public static clearContent() { rootViewInstance.setState({ content: null }); }render() { const { content } = this.state; return content && ( {content} ); } }const styles = StyleSheet.create({ container: { position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, zIndex: 999, flex: 1, } });

  1. 修改项目入口文件,将 RootView 组件添加为根元素的兄弟元素
import { AppRegistry } from 'react-native'; import App from './src/app'; import RootView from '@/components/RootView'; const createRootApp = () => () => ( ); AppRegistry.registerComponent(appName, createRootApp);

  1. 使用
RootView.setContent(我是一个RootView);

【手写|手写 React-Native 方法调用式的 Modal 弹框、Toast 提示】当然,这样需要修改入口文件,组件的封装性并不好;
那怎么样才能实现组件的封装呢?
我们可以把组件提取出来,并重写 AppRegistry 的 registerComponent 方法来实现组件的封装:
let rootViewInstance: RootView | undefined = undefined; class RootView extends Component { public constructor(props: {}) { super(props); rootViewInstance = this; }public readonly state = { content: null, }; public static setContent = (view: JSX.Element) => { rootViewInstance!.setState({ content: view }); }; public static clearContent = () => { rootViewInstance!.setState({ content: null }); }; public render() { const { content } = this.state; return content && ( {content} ); } }const registerComponentOld = AppRegistry.registerComponent; AppRegistry.registerComponent = (appKey, component) => { const createRootApp = () => { const OriginAppComponent = component(); // 获取原来的App根组件return () => ( ); }; return registerComponentOld(appKey, createRootApp); }; export default RootView; const styles = StyleSheet.create({ container: { position: 'relative', flex: 1, }, rootView: { position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, zIndex: 999, flex: 1, }, });

至此,一个简易的 RootView 组件就装好啦,如需 Modal、Toast 功能则可以基于此组件封装,比如一个 LoadingModal 组件:
import RootView from '@/components/RootView'; const LoadingModal = { show() { RootView.setContent( 加载中…… ); }, hide() { RootView.hide(); }, };

大功告成,可以愉快的玩耍啦 ~

    推荐阅读