在React项目中如何封装一个可扩展,复用性强的组件

大家好!今天给大家分享的是在React项目中如何封装一个可扩展,复用性强的组件。本篇以一个弹框组件为例。回顾我们在开发中常用的弹框有哪些特点:

  1. 一般弹框都是相对于屏幕上下左右居中的
  2. 弹框一般都会带有半透明的遮罩层
  3. 标题可以由使用者传入,有可能只是一串普通的文案,也有可能是一串html,甚至是一个小组件
  4. 内容可以自定模板
  5. 关闭按钮使用者可以控制是否显示
  6. 底部的取消按钮也可以由用户自己控制是否显示
  7. 点击关闭,取消,确认,有各自的事件响应

技术分析

一二两点非常简单就是一个普通的css布局,相信大家都清楚该怎么做,我们只要关注后面4点就可以了

  • 首先来看第3点,如果是在Vue 项目中,我们一定会想到用slot去做,并且使用具名slot,而在React项目中没有slot的概念。那么在React项目中怎样才能实现这个需求呢!其实非常简单,在React项目中我们可以直接通过props传递就可以了,在React项目中props可以传递任何类型的值,包括jsx,组件,普通文本,函数。
  • 接下来我们来看第4点,针对第4点如果是在Vue项目中我们肯定使用默认插槽来做,但是在React项目中就不行了,因为前面说过了React中没有插槽的概念, 但是React在props 上提供了一个特殊的属性,children,在子组件中通过children就可以获取到父组件中写在组件标签之间的内容,和Vue中的默认插槽看上去差不多。
  • 针对第5点和第6点就非常简单了,就是通过普通的props传递一个标识到子组件进行判断即可
  • 针对第7点,在Vue项目中我们可以利用自定义事件来完成这个功能,子组件通过emit触发自定义事件,从而在父组件得到各种事件的响应。而在React中没有自定义事件也没有emit,那么该怎么办呢!同样还是通过props,前面说过props 可以传递任何数据类型,所以函数也不例外。

代码实现(类组件实现)

1. 弹框组件

Dialog.js

js 复制代码
import React, { Component } from "react";
import "../assets/css/Dialog.css";

export default class Dialog extends Component {
  render() {
    return (
      <div className={this.props.show ? "dialog-wrap show" : "dialog-wrap"}>
        <div className="dialog">
          <div className="header">
            <h3>{this.props.title}</h3>
            {this.props.showClose ? (
              <span onClick={this.props.close} className="close">
                X
              </span>
            ) : (
              ""
            )}
          </div>
          <div className="content">{this.props.children}</div>
          <div className="footer">
            {this.props.showClose ? (
              <button className="btn cancel" onClick={this.props.cancel}>
                取消
              </button>
            ) : (
              ""
            )}
            {this.props.showConfirm ? (
              <button className="btn confirm" onClick={this.props.confirm}>
                确定
              </button>
            ) : (
              ""
            )}
          </div>
        </div>
      </div>
    );
  }
}

Dialog.css

css 复制代码
.dialog-wrap{
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0%;
  top: 0;
  background: rgba(0, 0, 0, 0.5);
  display: none;
}
.dialog-wrap.show{
  display: block;
}

.dialog {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: #fff;
  border-radius: 10px;
  width: 500px;
}

.dialog .header {
  height: 40px;
  line-height: 40px;
  font-size: 18px;
  display: flex;
  justify-content: space-between;
  padding: 0 20px;
  background: #409EFF;
  color: #fff;
}
.dialog h3{
  margin: 0;
  line-height: 40px;
}
.close {
  cursor: pointer;
}
.dialog .content {
  min-height: 100px;
  padding: 20px;
}

.dialog .footer {
  display: flex;
  justify-content: flex-end;
  padding: 0 20px 20px 20px;
}

.btn {
  background: none;
  outline: none;
  padding: 6px 12px;
  border: 1px solid #DCDFE6;
  border-radius: 4px;
  cursor: pointer;
}

.btn.cancel {
  margin-right: 20px;
}
.btn.confirm {
  background: #409EFF;
  border-color: #409EFF;
  color: #fff;
}

2. 使用Dialog

App.js

js 复制代码
import { Component } from "react";
import "./App.css";
import Dialog from "./components/Dialog";

class App extends Component {
  constructor() {
    super();
    this.state = {
      status: false,
      title: "删除",
    };
  }

  openDialog = () => {
    this.setState({
      status: true,
    });
  };
  confirm = () => {
    console.log("confirm");
    this.setState({
      status: false,
    });
  };
  cancel = () => {
    console.log("cancel");
    this.setState({
      status: false,
    });
  };
  close = () => {
    console.log("cancel");
    this.setState({
      status: false,
    });
  };
  render() {
    return (
      <div>
        <button className="btn" onClick={this.openDialog}>
          打开弹框
        </button>
        <Dialog
          title={this.state.title}
          show={this.state.status}
          confirm={() => this.confirm()}
          cancel={() => this.cancel()}
          close={() => this.close()}
          showCancel={true}
          showConfirm={true}
          showClose={true}
        >
          <div>你确定要删除吗</div>
        </Dialog>
      </div>
    );
  }
}

export default App;

App.css

css 复制代码
.btn {
  background: none;
  outline: none;
  padding: 6px 12px;
  border: 1px solid #DCDFE6;
  border-radius: 4px;
  cursor: pointer;
}

上述代码可以看到:

  • 在Dialog组件中我们通过父组件传过来的show属性来控制弹框的显示与否,在父组件中show的值由state上的status控制,当点击父组件中的打开弹框按钮触发openDialog事件将status改为true时,show也将改为true,弹框就显示了
  • 父组件通过向子组件传递confirm,cancel, close函数来响应点击事件,在子组件中就可以通过props获取到对应的函数
  • 父组件通过向子组件传递showCancel,showConfirm,showClose 属性来判断是否显示对应的按钮,在子组件中通过props 可以获取到对应的值,然后在render中通过三元表达式判断是否显示对应的按钮
  • 父组件中再调用子组件时在组件标签中写入了'你确定要删除吗',在子组件中通过this.props.children就可以获取到对应的内容

3. 演示效果

可以看到点击对应的事件也得到了对应的回应,符合预期。

函数组件,Hooks方式实现

在最新的React版本中比较流行函数组件和Hooks写法,下面我们将上述代码改成函数和Hooks来实现下。

Dialog.js

js 复制代码
import "../assets/css/Dialog.css";

export default function Dialog(props) {
  return (
    <div className={props.show ? "dialog-wrap show" : "dialog-wrap"}>
      <div className="dialog">
        <div className="header">
          <h3>{props.title}</h3>
          {props.showClose ? (
            <span onClick={props.close} className="close">
              X
            </span>
          ) : (
            ""
          )}
        </div>
        <div className="content">{props.children}</div>
        <div className="footer">
          {props.showClose ? (
            <button className="btn cancel" onClick={props.cancel}>
              取消
            </button>
          ) : (
            ""
          )}
          {props.showConfirm ? (
            <button className="btn confirm" onClick={props.confirm}>
              确定
            </button>
          ) : (
            ""
          )}
        </div>
      </div>
    </div>
  );
}

App.js

js 复制代码
import { useState } from "react";
import "./App.css";
import Dialog from "./components/Dialog";

function App() {
  const [status, setStatus] = useState(false);
  const [title] = useState("删除");

  const openDialog = () => {
    setStatus({
      status: true,
    });
  };
  const confirm = () => {
    console.log("confirm");
    setStatus(false);
  };
  const cancel = () => {
    console.log("cancel");
    setStatus(false);
  };
  const close = () => {
    console.log("cancel");
    setStatus(false);
  };
  return (
    <div>
      <button className="btn" onClick={openDialog}>
        打开弹框
      </button>
      <Dialog
        title={title}
        show={status}
        confirm={() => confirm()}
        cancel={() => cancel()}
        close={() => close()}
        showCancel={true}
        showConfirm={true}
        showClose={true}
      >
        <div>你确定要删除吗</div>
      </Dialog>
    </div>
  );
}

export default App;

可以看到将类组件改造成函数组件,将state 改成Hooks形式也非常简单

  • class 改成function
  • 将extends Component去掉
  • 将constructor 和里面的内容去掉
  • state 数据的定义和修改改成通过React中的useState中返回值结构出来的第一个值作为变量,第二个值作为修改的方法
  • 将render 方法去掉,直接return
  • 将return 中用到的this 直接去掉
  • 将子组件中的this.props通通改成参数函数参数上的props

来看下效果

可以看到显示依然正常。

总结

本篇通过一个弹框组件的封装,展示了在React中如何封装一个可扩展,复用性强的组件。并且和Vue实现方式进行对比,更容易掌握React组件的封装技巧和基础知识。然后在将类组件的形式改造成函数组件的形式,将state改成Hooks,进一步加深对React基础的巩固。

相关推荐
byzh_rc7 分钟前
[微机原理与系统设计-从入门到入土] 微型计算机基础
开发语言·javascript·ecmascript
m0_471199637 分钟前
【小程序】订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式
前端·vue.js·小程序
编程大师哥8 分钟前
Java web
java·开发语言·前端
A小码哥10 分钟前
Vibe Coding 提示词优化的四个实战策略
前端
Murrays10 分钟前
【React】01 初识 React
前端·javascript·react.js
大喜xi13 分钟前
ReactNative 使用百分比宽度时,aspectRatio 在某些情况下无法正确推断出高度,导致图片高度为 0,从而无法显示
前端
helloCat14 分钟前
你的前端代码应该怎么写
前端·javascript·架构
电商API_1800790524714 分钟前
大麦网API实战指南:关键字搜索与详情数据获取全解析
java·大数据·前端·人工智能·spring·网络爬虫
康一夏15 分钟前
CSS盒模型(Box Model) 原理
前端·css
web前端12316 分钟前
React Hooks 介绍与实践要点
前端·react.js