怎么在react里写一个弹框

2019-05-27 14:59:24 qq_39296163 阅读数 993

题目:在页面上有一个按钮,点击按钮出现一个旋转的loading,提交按钮不可点击,等待一秒后弹框显示提交失败或者提交成功,接下来我们直接开始
在此之前,我相信您已经安装好了react运行环境,如何创建react工程我就不再赘述,首先我们分析这个题目,按钮,弹框,两个子类组件,然后建一个父类来提供他们数据交流,接下来我们创建一个工程:
1.创建工程 first2react(项目名称)
在这里插入图片描述
2.在src文件夹下建好我们需要的组件弹框(Dialog),按钮(Button),父类(App),还有一个App.css文件来编写样式:
在这里插入图片描述
3.编写代码

按钮(Button):

在这里插入图片描述

接下来就是弹框(Dialog):

在这里插入图片描述
父类App:
import React,{Component} from “react”;
import Button from “./Button”;
import Dialog from “./Dialog”;
import “./App.css”

class App extends Component{
state={
loading:false,
dialog:false,
message:“xxx”
}

submit=()=>{
    this.setState({
        loading:true
    })
        // 模拟数据请求,假设数据请求经过一秒的到结果
    setTimeout(()=>{
        // 通过随机数的方式模拟可能出现的成功与失败两种结果
        const res=Math.random(1);
        if(res>0.5){
           this.setState({
                dialog:true,
                message:"提交成功!"
            })
        }else{
            this.setState({
                dialog:true,
                message:"提交失败!"
            })
        }
        this.setState({loading:false})
    },1000);
}


// 关闭事件
close=()=>{
    this.setState({
        dialog:false
    })
}

render(){
    const {loading,dialog,message}=this.state;

    return(
        <div>
            <Button loading={loading} submit={this.submit}>提交</Button>
            {dialog&&<Dialog message={message} close={this.close}/>}
        </div>
    )
}

}
export default App

编写css文件:
button {
background: none;
border: none;
outline: none;
width: 100px;
height: 30px;
border: 1px solid orange;
border-radius: 4px;
font-size: 16px;
display: block;
margin: 20px auto;
}

.loading {
display: inline-block;
width: 10px;
height: 10px;
border: 2px solid #ccc;
border-radius: 10px;
margin-right: 10px;
border-bottom: transparent;
border-top: transparent;
animation-name: loading;
animation-duration: 1s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}

.dialog-backdrop {
background: rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}

.dialog-container {
width: 300px;
background: #FFFFFF;
border-radius: 4px;
position: absolute;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);
padding: 10px;
}

.dialog-header {
height: 20px;
text-align: center;
line-height: 20px;
}

.dialog-body {
line-height: 1.6;
text-align: center;
margin-top: 20px;
}

.dialog-footer {
margin-top: 20px;
}
.dialog-footer button {
margin: 0 auto;
border: none;
background: orange;
color: #fff;
}

@keyframes loading {
from {
transform: rotate(0);
}

to {
    transform: rotate(360deg);
}

}

index.js文件代码:
在这里插入图片描述
4.运行
在这里插入图片描述点击按钮-》
在这里插入图片描述
50%的概率成功与否,这样一个简单的程序就完成了,相信很多学react的小伙伴都能看得懂这个代码,就是数据的传递,我个人倒觉得css有点难度,

2018-02-25 13:03:45 smk108 阅读数 8332

 

前端开发中经常使用弹框展示一些信息,如下图所示,点击show弹框显示。如果要实现点击hide及弹框外空白位置弹框消失,并且在点击alert弹框消失的同时响应alert上绑定的事件,要如何实现?

1、addEventListener

前端js开发中实现上述功能,首选实现方法肯定是使用addEventListener绑定事件,通过不同元素绑定的事件配合实现。但是这种实现方法在React中或许会导致意想不到的错误,不应该被推荐使用。

2、蒙层

这种方法是使用一层透明或半透明的div覆盖弹框下面的内容,并在蒙层上添加click事件,这种实现方法很常见,但是不能实现事件透传,即点击触发弹框消失的同时并不能触发下面绑定的事件。

css中将蒙层pointer-events属性设置为none可以实现事件的透传,但是会导致蒙层不能成为event的target,即不能绑定事件。如果可以接受事件不透传,这是一种很好的实现方法。

3、blur

我们可以通过弹框失去焦点事件触发弹框的消失,这种情况下点击到按钮位置是可以触发blur和按钮的点击事件的。这种方法需要特别注意的是需要给弹框最外层div元素设置tabindex属性,并且

弹框显示时调用focus使其获取到焦点。在弹框仅有展示功能或逻辑简单的情况下,这种方法同样是可以实现需求的,问题在于若弹框内可操作项很多,甚至有元素脱离文档流,blur方法就无能为力了。

4、mousedown&mouseup

mousedown&mouseup与click事件的区别在于只有在同一个元素上相继触发 mousedown 和 mouseup 事件,才会触发 click 事件;如果 mousedown 或 mouseup 中的一个被取消,就不会触发 click 事件。

我们可以借助这种差别,依旧使用蒙层覆盖弹框下的元素,并在蒙层上绑定mousedown事件,mousedown时蒙层和弹框消失,此时,弹框下的元素可以侦听到mouseup事件,就需要将原本绑定的click

事件修改为mouseup事件,这里有一个简单的例子:https://jsfiddle.net/smk108/fub5Lywq/ 。

这种实现方式会与使用click事件存在一定区别,主要表现在:

a.蒙层及弹框消失时机,click是在mouseup之后,而这种方式是在mousedown之后、mouseup之前;

b.原本的click事件在mousedown之后可以通过鼠标滑动,在其它元素上mouseup取消,这种方式不能取消。

目前,我只能总结到以上4种方式,但是都不能完美的实现需求,无论使用哪种方式都需要有所取舍。若以后能发现更好的实现方法我再补充,如果针对React中的实现,您有好的实现方法,敬请指教。

2019-05-20 14:23:11 qq_35757537 阅读数 1940

最近在用react开发项目,遇到一个需求——开发一个弹框组件。在react中创建一个组件是很简单的,只需要使用class创建并引入就可以了,但是要做到可以用js调用这个组件而不是写在jsx结构里,那就需要用到ReactDOM.render这个方法了。

首先先来屡一下需求:

  1. 弹框里的可配置字段:标题文字,提示文字,确认和取消按钮的显示隐藏以及文字。
  2. 点击确认和取消按钮后,可以触发相应的事件。
  3. 是否为短提示,短提示的时候,确认和取消按钮隐藏,并且2s后消失。

接下来用两种方法创建一个弹框组件,并比较一下这两种的差异。

 

下面先来实现一个普通的写在jsx结构里的组件:

弹框组件:DialogAlert.js

import React, { Component } from 'react';
import './index.scss';

class DialogAlert extends Component {
    constructor(props){
        super(props);
        this.state = {
            alertStatus:false,
            alertTitle:'提示', //标题
            alertTip:'网络错误', //提示
            cancelText:'取消',
            confirmText:'确认',

            isShortTip:false, //是否为短提示,短提示的情况下不显示'取消''确认'(且2s后消失),且优先级最高,其他配置无效

            isShowCancel:true, //是否显示确认按钮
            isShowConfirm:true, //是否显示确认按钮

            cancelCallbackFn:function(){}, //取消 回调函数
            confirmCallbackFn:function (){}//确认 回调函数
        }
    }

    componentWillReceiveProps(nextProps) {
        let options = nextProps.dialogOpt || {};

        //如果是短提示
        if(options.isShortTip){
            options.isShowCancel = false;
            options.isShowConfirm = false;
            setTimeout(()=>{
                this.close()
            },2000)
        }

        this.setState({
            ...options
        })
    }

    //取消
    cancel = () => {
        this.state.cancelCallbackFn();
        this.close()
    }
    //确认
    confirm = () => {
        this.state.confirmCallbackFn();
        this.close()
    }
    close = () => {
        this.setState({
            alertStatus:false
        })
    }

    render(){
        let opts = this.state;
        return (
            <div className="dialog-wrap" style={opts.alertStatus ? {display:'block'}:{display:'none'}}>
                <div className="dialog-box">
                    <h6>{opts.alertTitle}</h6>
                    <p>{opts.alertTip}</p>
                    {!opts.isShowCancel && !opts.isShowConfirm ? null : (
                        <div>
                            {opts.isShowCancel ? (<span onClick={ () => this.cancel() }>{opts.cancelText}</span>) : null}
                            {opts.isShowConfirm ? (<span className="confirm" onClick={ () => this.confirm() }>{opts.confirmText}</span>) : null}
                        </div>
                        )}
                </div>
            </div>
        )
    }
}

export default DialogAlert;

这里的数据更新用到了componentWillReceiveProps这个生命周期,当props发生变化时执行,初始化render时不执行,在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用。

调用页面index.js

在state中定义可配置字段的变量

import DialogAlert from '../../widget/DialogAlert/index';

//省略了组件的js

this.state = {
    dialogOpt:{
        alertStatus:false,
        alertTip:'我是自定义的内容',
        cancelText:'取消2',
        confirmText:'确认2',
        isShortTip:false,
        isShowCancel:true, //是否显示确认按钮
        isShowConfirm:true, //是否显示确认按钮
        cancelCallbackFn:function(){
          alert(0);
        }, //取消 回调函数
        confirmCallbackFn:function (){
          alert(1);
        }//确认 回调函数
      },
      //其他数据
    };

在jsx中埋好对应的组件结构

<div onClick={()=>(this.alertdialog())}>点击触发弹框</div>
<DialogAlert dialogOpt={this.state.dialogOpt}></DialogAlert>

添加触发事件

  alertdialog(){
    let opts = {
      alertStatus:true
    }
    let _dialogOpt = Object.assign(this.state.dialogOpt,opts)
    this.setState({
      dialogOpt:_dialogOpt
    })
  }

这样就完成一个普通的弹框。总感觉这样写的一个组件弹框有点冗余,复用起来也比较麻烦——在state里配置所有自定义的变量,并改动jsx结构,还需要注意写入jsx结构时弹框的层级问题。

 

接下来我们来实现一种可动态调用的组件:

原理是创建一个div,并插入到body里面,用这个div当容器,使用render渲染组件,通过改变组件的state来控制组件的显示和隐藏。

弹框组件:DialogAlert.js

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.scss';

//调用方法
// DialogAlert.open({
    // alertTitle:'提示2',
    // alertTip:"页面加载失败,是否重新加载?",
    // cancelText:'取消',
    // confirmText:'重新加载',
    // isShortTip:true,
    // isShowCancel:true,
    // isShowConfirm:true,
    // cancelCallbackFn:function(){
    //   console.log('取消了')
    // },
    // confirmCallbackFn:function (){
    //   console.log("确认了...");
    // }
// });

class DialogBox extends Component {
    constructor(props){
        super(props);
        this.state = {
            alertStatus: false, //是否显示提示框

            alertTitle:'提示', //标题
            alertTip:'网络错误', //提示
            cancelText:'取消',
            confirmText:'确认',

            isShortTip:false, //是否为短提示,短提示的情况下不显示'取消''确认'(且2s后消失),且优先级最高,其他配置无效

            isShowCancel:true, //是否显示确认按钮
            isShowConfirm:true, //是否显示确认按钮

            cancelCallbackFn:function(){}, //取消 回调函数
            confirmCallbackFn:function (){}//确认 回调函数
        }
    }

    //打开提示框
    open = (options) => {
        options = options || {};
        
        //如果是短提示
        if(options.isShortTip){
            options.isShowCancel = false;
            options.isShowConfirm = false;
            setTimeout(()=>{
                this.close()
            },2000)
        }

        options.alertStatus = true;
        this.setState({
            ...options
        })
    }
    //取消
    cancel = () => {
        this.state.cancelCallbackFn();
        this.close()
    }
    //确认
    confirm = () => {
        this.state.confirmCallbackFn();
        this.close()
    }
    close = () => {
        this.setState({
            alertStatus:false
        })
    }

    render(){
        let opts = this.state;
        return (
            <div className="dialog-wrap" style={opts.alertStatus? {display:'block'}:{display:'none'}}>
                <div className="dialog-box">
                    <h6>{opts.alertTitle}</h6>
                    <p>{opts.alertTip}</p>
                    {!opts.isShowCancel && !opts.isShowConfirm ? null : (
                        <div>
                            {opts.isShowCancel ? (<span onClick={ () => this.cancel() }>{opts.cancelText}</span>) : null}
                            {opts.isShowConfirm ? (<span className="confirm" onClick={ () => this.confirm() }>{opts.confirmText}</span>) : null}
                        </div>
                        )}
                </div>
            </div>
        )
    }
}

let div = document.createElement('div');
document.body.appendChild(div);
let DialogAlert = ReactDOM.render(<DialogBox /> ,div); //返回实例

export default DialogAlert;

调用页面index.js

import DialogAlert from '../../widget/DialogAlert/index';
   
//省略了组件的js

DialogAlert.open({
    alertTip:"加载失败,是否重新加载?",
    confirmText:'重新加载',
    cancelCallbackFn:()=>{
        window.history.back();
    },
    confirmCallbackFn:()=>{
        //todo...
    }
})

这里用到了ReactDOM.render,官方文档说这个方法目前会返回了对根组件实例的引用,所以我们可以调用到里面的open方法。但是官方文档中目前应该避免使用返回的引用,因为它是历史遗留下来的内容。为了以后的react更新迭代的兼容,我们可以省去动态插入组件的过程,改为写在jsx中,并设置ref,使用this.refs.xxx获取当前组件的实例,以便调用实例方法。

只需引入之后,直接调用就可以了。这样写的好处是解决了弹框的层级问题,也不用去改动jsx结构,其他页面复用起来更加方便快捷。

这两种方法在组件的定义上并没有很大的不同,只是在更新状态的时候有差异。第一种方法是在componentWillReceiveProps这个生命周期中监听父组件的值的变化再更新到state上,第二中方法是直接调用实例的open方法通过获取参数将值更新到state上。

 

2017-09-21 16:15:20 Z_yisha 阅读数 2642

本人菜鸟菜鸟,写的不对的地方欢迎指出!!!

1.在对dialog组件进行展示的时候,控制弹出框显示或者关闭的参数应该用props而不应该用state,例子父Login,子Dialog

父中:
<Login isShow={this.state.isShow} onClose={() => {this.setState({isShow:false})}}/>
子中,点击关闭按钮,调用如下方法
onCloseHandler(){
    this.props.onClose();
}
2.编写一个dialog组件,需要注意的是它的可复用性,比如哪些数据是需要用户自己来定义,如弹出框大小,弹出框的标题,以及弹出框内容等等
3.classnames
因为原生的添加多个className会报错,所以用到了classnames这个库,这个库的用法是
npm install classnames --save 安装,然后页面
import ClassNames from 'classnames'
现在就可以用啦,如className={ClassNames({"dialog-content":true})}
4.如何设置默认的props值和默认的state值
props:直接 组件名.defaultProps,如
Dialog.defaultProps = {
    width:550,
    height:500
};
state:在constructor中用this.state中设置就可以了
5.在登录组件中加了登录验证,这里直接可以用state来控制

如:

this.state = {
    info: ""
}
this.setState({info:'两次输入密码不一致'})

<label style={{color:'#FF0000',fontSize:'10px'}}>{this.state.info}</label>
最后附上此demo在git地址:https://github.com/yanzixi/test

2019-02-28 17:39:51 xiangzhihong8 阅读数 1076

在使用RN开发项目时,经常会遇到各种弹框,产品在设计时又是参照iOS来做的,效果如下:
在这里插入图片描述
在这里插入图片描述

基于此,我们就来封装一个这样的组件,下面是示例代码:

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import {
    StyleSheet,
    Text,
    View,
    TouchableOpacity,
    Dimensions,
    ScrollView,
    Modal
} from 'react-native';

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

export default class AlertView extends Component {

    static propTypes = {
        isShow: PropTypes.bool.isRequired, //控制视图是否显示,必需
        title: PropTypes.string,           //标题,可选
        message: PropTypes.string,         //文本内容,可选
        rightButton: PropTypes.object,     //底部右边按钮
        leftButton: PropTypes.object       //底部右边按钮
    }

    //蒙层背景
    renderMongoliaView() {
        return (
            <View style={styles.bgContainViewStyle}/>
        );
    }

    // 标题
    renderTitle() {
        return (
            <View style={styles.titleContainerStyle}>
                <Text style={styles.titleStyle}>{this.props.title}</Text></View>
        )
    }

    // 详情
    renderMessage() {
        return (
            <View style={styles.contentContainerStyle}>
                <Text style={styles.contentStyle}>{this.props.message}</Text></View>
        )
    }

    // 按钮
    renderBottomBtns() {
        let {leftButton, rightButton} = this.props

        let leftBtnText = leftButton && leftButton.text,
            leftBtnTextStyle = leftButton && leftButton.textStyle,
            leftBtnAction = leftButton && leftButton.onPress;

        let rightBtnText = rightButton && rightButton.text,
            rightBtnTextStyle = rightButton && rightButton.textStyle,
            rightBtnAction = rightButton && rightButton.onPress;

        if (leftBtnText && leftBtnText.length && rightBtnText && rightBtnText.length) {
            return (
                <View style={styles.btnContainerStyle}>
                    <TouchableOpacity onPress={() => {
                        leftBtnAction && leftBtnAction()
                    }}
                                      style={styles.btnStyle}>
                        <Text style={[{
                            fontSize: 16,
                            color: '#3981FD',
                            fontWeight: 'bold'
                        }, leftBtnTextStyle]}>{leftBtnText}</Text>
                    </TouchableOpacity>
                    <View style={styles.verticalLineStyle}/>
                    <TouchableOpacity onPress={() => {
                        rightBtnAction && rightBtnAction()
                    }}
                                      style={styles.btnStyle}>
                        <Text style={[{fontSize: 16, color: '#3981FD'}, rightBtnTextStyle]}>{rightBtnText}</Text>
                    </TouchableOpacity>
                </View>
            )
        } else {
            let text = leftBtnText;
            let click = leftBtnAction;
            let textStyle = leftBtnTextStyle

            if (rightBtnText && rightBtnText.length) {
                text = rightBtnText
                click = rightBtnAction
                textStyle = rightBtnTextStyle
            }
            if (!text || text.length === 0) {
                text = '确定'
            }
            return (
                <View style={styles.btnContainerStyle}>
                    <TouchableOpacity onPress={() => {
                        click && click()
                    }}
                                      style={styles.btnStyle}>
                        <Text style={[{fontSize: 16, color: '#157efb', fontWeight: 'bold'}, textStyle]}>{text}</Text>
                    </TouchableOpacity>

                </View>
            )
        }
    }

    // 绘制Alert视图
    renderAlertView() {
        let {title, message} = this.props
        return (
            <View style={styles.containerStyle}>
                <View style={[styles.alertViewStyle]}>
                    <ScrollView style={{marginTop: 20, marginBottom: 20}}>
                        {
                            (title && title.length)
                                ?
                                this.renderTitle()
                                :
                                null
                        }
                        {
                            (message && message.length)
                                ?
                                this.renderMessage()
                                :
                                null
                        }
                    </ScrollView>
                    <View style={styles.horizontalLineStyle}/>
                    {this.renderBottomBtns()}
                </View>
            </View>
        );
    }

    render() {
        if (!this.props.isShow) {
            return null;
        }
        return (
            <Modal transparent={true} onRequestClose={() => {
            }}>
                {
                    this.renderMongoliaView()
                }
                {
                    this.renderAlertView()
                }
            </Modal>
        )
    }
}

const styles = StyleSheet.create({
    containerStyle: {
        bottom: 0,
        top: 0,
        position: 'absolute',
        justifyContent: 'center',
        alignItems: 'center',
        width: width
    },
    bgContainViewStyle: {
        top: 0,
        width: width,
        position: 'absolute',
        opacity: 0.4,
        backgroundColor: 'rgb(0,0,0)',
        bottom: 0,
        justifyContent: 'center',
        alignItems: 'center'
    },
    alertViewStyle: {
        backgroundColor: 'white',
        borderRadius: 10,
        marginLeft: 50,
        marginRight: 50,
        position: 'absolute',
        maxHeight: height - 40
    },
    titleContainerStyle: {
        justifyContent: 'center',
        alignItems: 'center',
        marginLeft: 15,
        marginRight: 15,
        marginBottom: 10,
    },
    titleStyle: {
        fontSize: 17,
        fontWeight: 'bold',
        textAlign: 'center',
        color: '#333333'
    },
    contentContainerStyle: {
        justifyContent: 'center',
        alignItems: 'center',
    },
    contentStyle: {
        justifyContent: 'center',
        marginLeft: 20,
        marginRight: 20,
        fontSize: 14,
        textAlign: 'center',
        color: '#666666',
    },
    horizontalLineStyle: {
        height: 0.5,
        backgroundColor: 'lightgrey'
    },
    btnContainerStyle: {
        flexDirection: 'row',
        width: width - 100,
        height: 48
    },
    verticalLineStyle: {
        height: 48,
        backgroundColor: 'lightgrey',
        width: 0.5
    },
    btnStyle: {
        flex: 1,
        height: 47,
        justifyContent: 'center',
        alignItems: 'center'
    },

});

然后在需要使用的地方,按照propTypes传入对应的字段即可。

单选按钮:

renderFocusTip() {
        const {showAlert} = this.state;
        return (
            <AlertView isShow={showAlert}
                       title={'关注成功'}
                       message={'您可在"我的"-"我的医生"里查看已关注医生!'}
                       leftButton={{
                           'text': '我知道了', onPress: () => {
                               this.setState({
                                   showAlert: false
                               })
                           }
                       }}
            />
        );
    }

多选按钮:

renderAlertView() {
        if (!this.state.isShowAlert) return null;

        const {content} = this.state.popContent || {}
        let leftButton = {
            text: '取消', onPress: () => {
                this.setState({
                    isShowAlert: false
                })

                let trackParam = {
                    entrance: 'doctor_main',
                    doctor_id: this.props.doctorId,
                    dept_name: this.getDeptName()
                }
                TrackManager.trackEvent('pajk_app_jump_query_consult_popup_cancel_click', null, trackParam)
            },
            textStyle: {
                fontWeight: 'normal'
            }
        };
        let rightButton = {
            text: '继续', onPress: () => {
                let trackParam = {
                    entrance: 'doctor_main',
                    doctor_id: this.props.doctorId,
                    dept_name: this.getDeptName()
                }
                TrackManager.trackEvent('pajk_app_jump_query_consult_popup_continue_click', null, trackParam)

                this.setState({
                    isShowAlert: false
                })

                this.confirmAssignNewDoctor && this.confirmAssignNewDoctor()
            }
        }
        return (
            <AlertView isShow={this.state.isShowAlert}
                       message={content || ''}
                       leftButton={leftButton}
                       rightButton={rightButton}/>
        )
    }