前端基础之《React(16)—Modal组件封装》

一、组件化

1、使用组合思想来实现组件化

在react中组件的复用,是组合还是继承?是组合

2、一个正常的组件有哪些东西

3、字段校验

(1)一般校验两种:必填非必填,是什么类型

(2)PropTypes.element(react元素类型):包含jsx、string、null、undifined

(3)必填属性使用isRequired来约束,非必填属性一般给默认值

(4)所有属性都要给类型约束

(5)PropTypes.oneOf('primary', 'danger', 'info'):取值是其中之一

(6)oneOfType(PropTypes.bool,PropTypes.instanceOf(Array), PropTypes.element):取值类型是其中之一

(7)PropTypes.node:比PropTypes.element范围更广

(8)指定user这个对象类型的结构

user: PropTypes.shape({

name: PropTypes.string.isRequired,

age: PropTypes.number,

})

4、函数命名规则

(1)如果一个函数参与视图渲染,前面不加on

(2)如果一个函数跟事件有关,前面加on

5、什么是render props

(1)在封装React组件时,如果一个props属性其值是函数类型并返回JSX视图结果,那么这个props属性就是render props,即,可以参与组件的视图渲染。例如:<Modal footer={()=>{xxx}}。这个footer属性就被称为render props

(2)render props在组件化中,用的非常多

6、{...props}

属性穿透

父组件给的props,直接给到子组件

7、Protal

(1)protal提供了一种将子节点渲染到存在于父组件以外的DOM节点的方案,比如我要将弹窗挂载到body上

(2)可以把一段JSX结构,"穿梭"插入到父级节点中的一种能力

语法:ReactDOM.createPortal(<jsx/>,dom节点)

8、到底什么是组合,怎么理解组合

组合是一种设计模式,它是React组件化的基础

9、如何封装组件

第一步,根据UI视图拆解结构,得到一个一个可独立封装的小组件

第二步,把这些独立的小部件分别封装

第三步,再使用props把这些小组件串联起来

二、例子

javascript 复制代码
import '@/assets/modal.scss';
import PropTypes from 'prop-types';
import { useState } from 'react';
import { createPortal } from 'react-dom';

// 封装button组件
function Button (props) {
    const { type, children, onClick } = props;
    return (
        <div className='self-button'>
            <span className={type} onClick={onClick}>{children}</span>
        </div>
    );
}

Button.propTypes = {
    type: PropTypes.oneOf(['default', 'primary', 'danger', 'info']),
    children: PropTypes.string,
    onClick: PropTypes.func,
};
Button.defaultProps = {
    type: 'default',
    children: '点击按钮',
};

// 封装组件
function Modal (props) {
    const { title, closable, children, type, visible, onCancel, onOk, onDelete, footer } = props;
    const randerFooter = () => {
        switch (type) {
            case 'confirm':
                return (
                    <div>
                        <Button type="primary" onClick={onOk}>确认</Button>
                        <Button onClick={onCancel}>取消</Button>
                    </div>
                );
            case 'danger':
                return (
                    <div>
                        <Button type="danger" onClick={onDelete}>删除</Button>
                        <Button onClick={onCancel}>取消</Button>
                    </div>
                );
            case 'info':
                return (
                    <div>
                        <Button type="info" onClick={onOk}>确认</Button>
                        <Button onClick={onCancel}>取消</Button>
                    </div>
                );
            default:
                return (
                    <div>
                        <Button type='info' onClick={onCancel}>我知道了</Button>
                    </div>
                );
        }
    };
    const handlerLayer = (ev) => {
        console.log('---ev.currentTarget', ev.currentTarget.dataset);
        console.log('---ev.target', ev.target.dataset);
        if (ev.target.dataset.self === 'layer') { // 防止事件冒泡到父元素
            onCancel();
        }
    };

    return (
        <div 
            className="layer" 
            style={{ display: visible ? 'block' : 'none' }} 
            data-self="layer" 
            onClick={handlerLayer}>
            <div className="modal">
                <header>
                    <div>
                        <div>{title}</div>
                        <div onClick={onCancel}>{closable && 'X'}</div>
                    </div>
                </header>
                <main>{children}</main>
                <footer>
                    {footer ? footer() : randerFooter()}
                </footer>
            </div>
        </div>
    );
}

// 页面组件
function A () {
    const [show, setShow] = useState(false);
    const submit = () => {
        // 异步调接口
        setTimeout(() => {
            console.log('---submit 异步调接口');
            setShow(false);
        },1000);
    };

    return createPortal(
        <div>
            <button onClick={() => setShow(true)}>测试弹框</button>
            <Modal 
                visible={show} // 对话框是否可见
                title={<div>警告</div>}  // 对话框标题
                closable={true} // 是否可关闭
                type='info' // 对话框类型
                onCancel={() => setShow(false)} //点击遮罩层或右上角叉或取消按钮的回调
                onOk={() => submit()}
                onDelete={() => submit()}
                footer={() => { // 用户自定义footer
                    return [
                        <Button type="primary" key='1'>确认1</Button>,
                    ];
                }}
            >
                用户名:<input type="text" />
            </Modal>
            
        </div>,
        document.body
    );
}

export default A;

// 属性校验
Modal.propTypes = {
    title: PropTypes.elementType,
    closable: PropTypes.bool,
    children: PropTypes.node, // children是Modal组件标签内的内容
    // xx: PropTypes.oneOf(['primary', 'danger', 'info']),
    // yy: oneOfType([PropTypes.bool,PropTypes.instanceOf(Array), PropTypes.element]),
    type: PropTypes.oneOf(['confirm', 'danger', 'info']),
    visible: PropTypes.bool,
    onCancel: PropTypes.func,
    onOk: PropTypes.func,
    onDelete: PropTypes.func,
    footer: PropTypes.func,
};
// 默认值
Modal.defaultProps = {
    title: '标题',
    closable: true,
    children: <div>请编写主体内容</div>,
    type: 'info',
    visible: false,
    onCancel: () => {},
    onOk: () => {},
    onDelete: () => {},
};
相关推荐
雨季mo浅忆2 小时前
Vue关于前端XSS漏洞修复方案
前端·xss
切糕师学AI2 小时前
el-select-v2 深度解析:用虚拟滚动解决海量数据下拉选择性能瓶颈
前端·vue.js·elementui·el-select-v2
徐小夕3 小时前
JitOffice:把办公、AI 与审批装进同一个平台
前端·vue.js·github
徐小夕3 小时前
万字分享:为什么我们要自研Word解析引擎?JitWord 解析引擎 4.0 深度复盘
前端·vue.js·github
陈随易3 小时前
副业搞钱点子之独立开发
前端·后端·程序员
console.log('npc')3 小时前
问题跟踪与知识沉淀:从任务到知识的闭环)
前端·javascript·人工智能
恋猫de小郭3 小时前
最近 Android 有什么更新:Android Dev Summit 2026 技术解读
android·前端·flutter
计算机魔术师3 小时前
Reddit 宣布转向以登录为主的体验,以应对 AI 搜索带来的流量下滑
前端
熊猫钓鱼>_>3 小时前
上架审核常见驳回与隐私合规清单:过审前先自查这 8 条【鸿蒙心迹】
开发语言·前端·华为·华为云·harmonyos·openharmony·ai制作