在使用 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 。
实现步骤:
- 创建一个 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,
}
});
- 修改项目入口文件,将 RootView 组件添加为根元素的兄弟元素
import { AppRegistry } from 'react-native';
import App from './src/app';
import RootView from '@/components/RootView';
const createRootApp = () => () => (
);
AppRegistry.registerComponent(appName, createRootApp);
- 使用
RootView.setContent(我是一个RootView );
当然,这样需要修改入口文件,组件的封装性并不好;
那怎么样才能实现组件的封装呢?
我们可以把组件提取出来,并重写 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();
},
};