基于react和antd二次封装公共搜索组件
前言
你是否曾经在使用 React 和 Ant Design 构建前端应用时,发现自己反复编写相似的组件代码?是否曾感到组件的维护和定制变得繁琐?如果是的话,你并不孤单,因为这是前端开发中常见的问题之一。
在本篇技术分享博客中,我们将探讨如何通过二次封装 Ant Design 组件来提高 React 应用的开发效率和可维护性。我们将分享实用的技巧和最佳实践,帮助你更好地理解组件封装的重要性,以及如何精确地进行二次封装,使你的项目变得更加高效、灵活和易于维护。
无论你是 React 初学者还是经验丰富的前端工程师,本博客都将为你提供有价值的见解。让我们一起深入探讨这个令人兴奋的主题,为构建更好的前端应用打下坚实的基础。
提示:以下是本篇文章正文内容,下面案例可供参考
一、为什么 React 组件封装很重要?
React 组件封装是前端开发中的关键概念,它涉及将可复用的 UI 元素打包成独立的组件,以便在整个应用程序中重复使用。以下是一些关于 React 组件封装重要性的观点,可以包括在你的文章中:
-
代码复用和可维护性: React 组件封装允许你将功能性和界面元素封装在一个独立的单元中。这意味着你可以轻松地在项目中重复使用这些组件,从而减少了重复编写相似代码的需求。这不仅提高了代码复用性,还使代码更易于维护,因为你只需在一个地方进行更改,即可在整个应用程序中应用更新。
-
可定制性: 通过封装组件,你可以提供一组可配置的属性(props),使组件能够满足不同项目的需求。这增加了组件的灵活性,可以根据不同情况进行自定义。
-
降低错误率: 组件封装有助于减少人为错误的发生。当你构建并测试一个组件一次后,它可以在多个地方使用,减少了复制粘贴代码可能导致的错误。
-
团队协作: 在团队环境中,组件封装有助于提高开发者之间的协作效率。团队成员可以更容易地理解和使用封装的组件,而不必深入了解组件的内部工作方式。
-
生态系统: React 社区拥有庞大的第三方组件库,这些库通常是通过组件封装来实现的。通过了解组件封装的原理,你可以更好地利用和贡献给这些生态系统。
-
未来扩展性: 随着项目的增长和演化,你可能需要对组件进行更新和扩展。良好封装的组件更容易扩展和维护,从而有助于项目的长期成功。
二、使用步骤
1.引入库
代码如下(示例):
// 引入 React 和 antd 的组件
import React, { Component, Fragment } from 'react';
import { Form, Select, Input } from 'antd';
const FormItem = Form.Item;
const Option = Select.Option;
@Form.create()
2.封装公共组件
代码如下(示例):
import React, { Component, Fragment } from 'react';
import { Form, Input, InputNumber, Select, Button, Icon } from 'antd';
const { FormItem } = Form;
const { Option } = Select;
export default class Search extends Component {
state = {
expandForm: false, // 控制搜索表单的展开和收起状态,默认为收起
};
minInput = {}; // 用于保存最小值输入框的引用
maxInput = {}; // 用于保存最大值输入框的引用
componentDidMount() {
let { expandForm } = this.props;
// 如果从父组件传递了expandForm属性,则在组件挂载时设置表单展开状态
if (expandForm != undefined) {
this.setState({ expandForm });
}
}
// 用于清空表单内容的生命周期钩子
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.isReset != undefined && nextProps.isReset) {
// 如果从父组件传递了isReset属性且为true,则重置表单字段
this.props.form.resetFields();
}
}
// 数字输入框的值改变事件处理函数
inputnumOnChange = (e, val, type) => {
if (val.onChange) {
// 如果输入框定义了onChange属性,则调用它,并传递事件和类型
val.onChange(e, type);
}
};
// 封装输入框和下拉选择框组件的渲染函数
renderFormItem = (val, index) => {
const { form: { getFieldDecorator } } = this.props;
if (val.type === 'input') {
// 渲染输入框
return (
<FormItem key={index} style={{ width: 230 }} label={val.label}>
{getFieldDecorator(val.name)(<Input maxLength={250} style={{ width: 150 }} placeholder={val.placeholder} />)}
</FormItem>
);
} else if (val.type === 'inputnum_double') {
// 渲染数字输入框(双值)
return (
<FormItem key={index} style={{ width: val.width ? val.width : 270 }} label={val.label}>
<div style={{ display: 'flex' }}>
{getFieldDecorator(val.name1, {
initialValue: val.initialValue1,
})(<InputNumber
style={{ width: val.inputWidth != undefined ? val.inputWidth : 80 }}
placeholder={val.placeholder}
min={val.min !== undefined ? val.min : 0}
max={val.max !== undefined ? val.max : 9999999}
precision={val.precision != undefined ? val.precision : 2}
onChange={(e) => this.inputnumOnChange(e, val, 'min')}
/>)}
<span style={{ width: 10, color: '#333333', textAlign: 'center' }}>-</span>
{getFieldDecorator(val.name2, {
initialValue: val.initialValue2,
})(<InputNumber
style={{ width: val.inputWidth != undefined ? val.inputWidth : 80 }}
placeholder={val.placeholder}
min={val.min !== undefined ? val.min : 0}
max={val.max !== undefined ? val.max : 9999999}
precision={val.precision != undefined ? val.precision : 2}
onChange={(e) => this.inputnumOnChange(e, val, 'max')}
/>)}
</div>
</FormItem>
);
} else if (val.type === 'select') {
// 渲染下拉选择框
return (
<FormItem key={index} style={{ width: val.width != undefined ? val.width : 230 }} label={val.label}>
{getFieldDecorator(val.name, {
rules: val.rules,
})(
<Select
placeholder={val.placeholder}
style={{ width: val.selectWidth != undefined ? val.selectWidth : 150 }}
getPopupContainer={(triggerNode) => triggerNode.parentNode}
onChange={(value) => {
// 下拉选择框的值改变时的事件处理函数
this.sldHandSeleChange(val, value);
if (val.step === 1) {
// 如果定义了步骤为1,重置整个表单字段
this.props.form.resetFields();
} else if (val.step === 2) {
// 如果定义了步骤为2,重置指定字段
this.props.form.resetFields(['cateId3']);
}
}}
>
{val.sel_data != null &&
val.sel_data.map((items, indexs) => {
return (
<Option
key={indexs}
value={val.diy != undefined && val.diy ? items[val.sele_key] : items.key}
>
{val.diy != undefined && val.diy ? items[val.sele_name] : items.name}
{val.remark !== undefined &&
items[val.remark] !== undefined &&
items[val.remark] !== null &&
items[val.remark] !== '' ? (
'(' + items[val.remark] + ')'
) : (
''
)}
</Option>
);
})}
</Select>
)}
</FormItem>
);
}
};
// 搜索重置事件处理函数
handleFormReset = () => {
const { form } = this.props;
form.resetFields();
// 调用父组件传递的seaReset函数
this.props.seaReset();
};
// 搜索事件处理函数
handleSearch = (e) => {
e.preventDefault();
const { form } = this.props;
form.validateFieldsAndScroll((err, fieldsValue) => {
if (err) return;
// 处理组件的值
if ((fieldsValue.hasOwnProperty('marketPriceEnd') && fieldsValue.hasOwnProperty('marketPriceStart')) {
this.props.form.resetFields([
'marketPriceEnd','marketPriceStart'
])
}
// 调用父组件传递的seaSubmit函数,将表单字段值作为参数传递
this.props.seaSubmit(fieldsValue);
});
};
// 渲染搜索表单的前半部分,索引小于4的表单项
renderSearchFirst = (search_data) => {
return search_data.map((item, index) => {
// 如果表单项的索引小于4,调用renderFormItem函数渲染该表单项
return index < 4 ? this.renderFormItem(item, index) : null;
});
};
// 渲染搜索表单的后半部分,索引大于3的表单项
renderSearchSecond = (search_data) => {
return search_data.map((item, index) => {
// 如果表单项的索引大于3,调用renderFormItem函数渲染该表单项
return index > 3 ? this.renderFormItem(item, index) : null;
});
};
// 切换搜索表单的展开和收起状态
toggleForm = () => {
const { expandForm } = this.state;
this.setState(
{
expandForm: !expandForm,
},
() => {
// 如果定义了moreSearchToggle属性,则调用它
if (this.props.moreSearchToggle) this.props.moreSearchToggle();
}
);
};
// 渲染搜索表单
renderSimpleForm() {
const { search_data, top } = this.props;
const { expandForm } = this.state;
return (
search_data.length > 0 && (
<Form onSubmit={this.handleSearch} ref={'sld_common_search_part'} layout="inline">
{this.renderSearchFirst(search_data)}
{expandForm && (
<Fragment>
{this.renderSearchSecond(search_data)}
</Fragment>
)}
<span style={{ position: 'absolute', right: search_data.length > 4 ? 20 : 0, top: top != undefined ? top : 0 }}>
<Button type="primary" htmlType="submit">
搜索
</Button>
<Button style={{ marginLeft: 3, marginBottom: 10 }} onClick={this.handleFormReset}>
重置
</Button>
{search_data.length > 4 && (
<a style={{ marginLeft: 3, fontSize: 12, color: '#FF6A12' }} onClick={this.toggleForm}>
{expandForm ? '收起' : '展开'} <Icon type={expandForm ? 'up' : 'down'} />
</a>
)}
</span>
</Form>
)
);
}
render() {
return this.renderSimpleForm();
}
}
3.用法示例
代码如下(示例):
import React from 'react';
import Search from '@/Search'; // 导入外部的搜索组件
@Form.create() // 使用装饰器创建一个表单
export default class GoodsList extends Component {
constructor(props) {
super(props);
this.state = {
formValues: {}, // 存储搜索条件的状态
search: [
{
type: 'input',
label: '商品名称',
name: 'goodsName',
placeholder: '请输入商品名称',
},
{
type: 'inputnum_double',
label: '市场价',
name1: 'highPrice', // 输入框的最小值字段
name2: 'PriceEnd', // 输入框的最大值字段
initialValue1: '', // 初始的最小值
initialValue2: '', // 初始的最大值
placeholder: '请输入',
onChange: (val, type) => {
// 处理市场价范围的输入变化事件
if (type === 'min') {
let { search } = this.state;
search[1].initialValue1 = val;
this.setState({ search });
} else if (type === 'max') {
let { search } = this.state;
search[1].initialValue2 = val;
this.setState({ search });
}
},
},
{
type: 'select',
label: '商品类型',
name: 'commodityType',
placeholder: '请选择商品类型',
sel_data: [
{ key: ' ', name: '全部' },
{ key: '1', name: '虚拟' },
{ key: '2', name: '实物' },
],
},
],
};
}
// 组件卸载时移除窗口大小改变的事件监听
componentWillUnmount() {
window.removeEventListener('resize', this.resize);
}
// 监听窗口大小变化的函数
resize = () => {
const { search_height } = this.state;
if (this.refs.search_part !== undefined) {
if (this.refs.search_part.clientHeight !== search_height) {
this.setState({ search_height: this.refs.search_part.clientHeight });
}
}
};
// 搜索事件处理函数
search = (data) => {
let { search, formValues } = this.state;
const values = { ...data };
// 如果市场价的最小值大于最大值,则交换它们
if (
search_data[1].initialValue1 !== '' &&
search_data[1].initialValue2 !== '' &&
search_data[1].initialValue1 > search_data[1].initialValue2
) {
let temp = search_data[1].initialValue1;
search_data[1].initialValue1 = search_data[1].initialValue2;
search_data[1].initialValue2 = temp;
let temps = values.highPrice;
values.highPrice = values.PriceEnd;
values.PriceEnd = temps;
}
// 更新状态,包括搜索数据、搜索条件和参数
this.setState({
search,
formValues: values,
params: { pageSize: 10 },
});
// 调用获取列表数据的函数,并传入参数
this.get_list({ pageSize: 10, ...values });
};
// 搜索重置事件处理函数
seaReset = () => {
let { search } = this.state;
// 将市场价的最小值和最大值重置为空
search_data[1].initialValue1 = '';
search_data[1].initialValue2 = '';
// 清空搜索条件
this.setState({
search,
formValues: {},
params: { pageSize: 10 },
});
// 调用获取列表数据的函数,并传入参数
this.get_list({ pageSize: 10 });
};
// 点击展开/折叠更多搜索条件
moreSearchToggle = () => {
const { search_height } = this.state;
if (this.refs.search_part !== undefined) {
if (this.refs.search_part.clientHeight !== search_height) {
this.setState({ search_height: this.refs.search_part.clientHeight });
}
}
};
render() {
const { search, search_height } = this.state;
return (
<div ref={'search_part'}>
{/* 渲染搜索组件,传递搜索数据、展开/折叠搜索条件、提交搜索和重置搜索的回调函数 */}
<Search
search_data={search}
moreSearchToggle={() => this.moreSearchToggle()}
seaSubmit={(data) => this.search(data)}
seaReset={() => this.seaReset()}
/>
</div>
);
}
}
你可以根据我的写法加上其他的antd组件
4.样式示例
总结
总而言之,React 组件封装是前端开发中不可或缺的一部分。在本文中,我们探讨了为什么它如此重要,并提供了一些有关如何更好地封装和应用组件的实用技巧。
通过组件封装,我们能够实现代码的复用,提高可维护性,减少错误,并更好地协作。它不仅可以在个人项目中发挥作用,还有助于更广泛的 React 社区生态系统的发展。
我鼓励你在你的下一个 React 项目中尝试这些技巧,并深入了解组件封装的概念。随着你的经验的增长,你将发现它们是构建出色前端应用的关键。
无论你是初学者还是有经验的开发者,都应该牢记 React 组件封装的价值,以便在不断变化的前端开发领域中脱颖而出。 谢谢你阅读这篇文章,希望它对你有所帮助!