在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基础的巩固。

相关推荐
鱼樱前端6 分钟前
今天介绍下最新更新的Vite7
前端·vue.js
coder_pig1 小时前
跟🤡杰哥一起学Flutter (三十四、玩转Flutter手势✋)
前端·flutter·harmonyos
万少1 小时前
01-自然壁纸实战教程-免费开放啦
前端
独立开阀者_FwtCoder1 小时前
【Augment】 Augment技巧之 Rewrite Prompt(重写提示) 有神奇的魔法
前端·javascript·github
yuki_uix1 小时前
AI辅助网页设计:从图片到代码的实践探索
前端
我想说一句1 小时前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
陈随易1 小时前
MoonBit助力前端开发,加密&性能两不误,斐波那契测试提高3-4倍
前端·后端·程序员
汤姆Tom1 小时前
JavaScript reduce()函数详解
javascript
小飞悟1 小时前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子1 小时前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript