安卓怎么实现根据点击位置出现提示框_React Naitve 中实现简易版Portal

本文讲述了在ReactNative开发中遇到的Android设备上组件超出父组件边界无法显示的问题,以及如何利用ContextAPI模拟实现Portal功能来解决这个问题。通过创建PortalContext、PortalProvider、PortalGate和wrapWithTeleport等组件,实现了在不同组件间传送ReactNode,确保在Android上也能正常渲染。
摘要由CSDN通过智能技术生成

在Mobile App开发中,一个很常见的UI设计就是弹出提示框。比如,当用户输入有误时,我们希望从顶部向下划出一个提示,如图所示,当用户没有输入用户名和密码就点击登陆后,会提示出相应的错误。

132a7e4c0d802f478deb632193968450.png

当我们用React Native去开发这个功能时,首先能够想到的一种方式就是,在组件中设置一个状态,当它为true时,渲染出提示框;当为false时,隐藏提示框,具体怎么实现相信大家都是没问题的。

但是当我测试这个功能的时候,问题出现了。我发现在iOS中,一切正常,但是在Android手机中,就是看不到提示框,这又是问什么呢?后来发现,问题出在Android对于<View>的显示方式和iOS不同,任何超出<View>边界的东西都会被隐藏掉,相当于被默认设置了overflow: hidden一样

在我的组件结构中,红色边框内是名为Login的组件,其中有个状态变量isErrorShown,当它为true时,就会渲染<ErrorAlert />这样一个组件出来,那么很显然,<ErrorAlert />就是<Login />的子组件,它也同样是属于这个红色边框以内的,当我通过css设置,把它放在窗口顶部时,它就已经超出了红色边界,在Android上就会被隐藏起来。

d801da17bd47a5a8b7f6374c915b4672.png

那么该如何解决这个问题呢?我们可能会想到把<ErrorAlert />放在<Login />的父组件中(当然也可能是爷爷辈的组件),让它脱离红色框框的管辖,然后把isErrorShown放在Redux中管理。这当然也是一种办法,但是当有多种类型的alert时,就都要在Redux中设置相应的变量;此外alert放置的位置也不一定都是固定在窗口顶端,可能有的时候就需要把它放置在某个组件的顶部或者底部,那么承载它的组件就不得不去connect Redux,在我看来,这种方法可能有点麻烦。

那么除了利用Redux还有其他的办法的么?当然有,对React熟悉的同学可能想到了利用Portal这个特性,具体的使用方法大家可以参看文档,简单的理解,Portal就像一个传送门,可以在不同组件之间传送React Node,你只需要指定好接收React Node的domNode就可以了。那么解决方案已经出来了,只需要在isErrorShown时,把<ErrorAlert />传送到<Login />的某个父组件中去渲染就可以了。

正当我沾沾自喜,庆幸找到解决方案的时候,坑来了!React Native中竟然不支持Portal。。。心中一万头草泥马崩腾而过。。。好吧,让我们利用Context API实现一个类似Portal的功能,代码如下:

PortalContext.js

// @flow
import * as React from 'react';

export default React.createContext({
  gates: {}, // key: 挂载点名称,value: 需要渲染的React.Node
  teleport: (gateName: string[], element: React.Node) => {}, //将需要渲染的element,传送到对应的挂载点上
});

PortalProvider.js

// @flow

import * as React from 'react';
import PortalContext from './PortalContext';

type Props = {
  children: React.Node,
}

type State = {
  gates: {
    [key: string]: React.Node,
  }
}

class PortalProvider extends React.Component<Props, State> {
  state = {
    gates: {}, // 存储着一一对应的挂载点名称和React.Node
  }

  teleport = (gateName: string[], element: React.Node) => { // 运行teleport时,将传来的ReactNode存储在gates中
    const { gates } = this.state;
    const newGates = gateName.reduce((acc, name) => {
      acc[name] = element;
      return acc;
    }, {});
    this.setState({
      gates: { ...gates, ...newGates},
    });
    return this.teleport;
  }

  render() {
    const { children } = this.props;
    const { gates } = this.state;
    return (
      <PortalContext.Provider value={{ gates, teleport: this.teleport }}>
        {children}
      </PortalContext.Provider>
    );
  }
}

export default PortalProvider;

PortalGate.js

// @flow

import * as React from 'react';
import PortalContext from './PortalContext';

type Props = {
  gateName: string,
  children?: (teleport: (gateName: string[], element: React.Node) => void) => React.Node,
}

function PortalGate(props: Props) { // 根据挂载点名称,从gates中取出对应的ReactNode进行渲染
  const { gateName, children } = props;
  return (
    <PortalContext.Consumer>
      {
        (value) => {
          return (
            <React.Fragment>
              {value.gates[gateName]}
            </React.Fragment>
          );
        }
      }
    </PortalContext.Consumer>
  );
}

export default PortalGate;

wrapWithTeleport.js

import React from 'react';
import PortalContext from './PortalContext';

const wrapWithTeleport = WrappedComponent => props => ( // HOC,向组件中注入teleport方法,让组件能够有传送能力
  <PortalContext.Consumer>
    {(value) => {
      const { teleport } = value;
      return <WrappedComponent teleport={teleport} {...props} />;
    }}
  </PortalContext.Consumer>
);

export default wrapWithTeleport;

使用方法如下:

当点击Button后,Login中运行teleport方法,将<ErrorAlert />传送到gateName为’errorAlert`的PortalGate组件中去渲染。这样<ErrorAlert />就可以渲染在<Login />外面,Android中就可以正常显示了。

App.js

const App = () => {
  return (
    <PortalProvider>
      <PortalGate gateName="errorAlert" />
      <Login />
    </PortalProvider>
  )
}

export default App;

Login.js

const Login = (props) => {
  const { teleport } = props;

  const handleOnPress = () => {
    teleport(['errorAlert'], <ErrorAlert />);
  }

  return (
    <View>
      ...
      <Button onPress={handleOnPress} />
      ...
    </View>    
  )
}

export default wrapWithTeleport(Login);

以上就是React Native中实现类似portal效果的方法,大家可以利用它,把组件渲染在任何需要的地方。完结,撒花!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值