前言
我是歌谣 我有个兄弟 巅峰的时候排名c站总榜19 叫前端小歌谣 曾经我花了三年的时间创作了他 现在我要用五年的时间超越他 今天又是接近兄弟的一天人生难免坎坷 大不了从头再来 歌谣的意志是永恒的 放弃很容易 但是坚持一定很酷 微信公众号前端小歌谣
需求分析
首先 我们需要实现一个带有搜索功能的搜索框 本次只实现两种框的搜索功能 一种input 一种select
功能思维

第一步 初始版本
先写出一个input和一个render 还有两个按钮
| <Form.Item |
| label="测试数据" |
| key="1" |
| name="测试数据" |
| rules={xxx} |
| style={xxx} |
| > |
| {true ? <Select/> : <Input />} |
| </Form.Item> |
| <Form.Item> |
| <Button htmlType="submit">查询</Button> |
| </Form.Item> |
| <Form.Item> |
| <Button htmlType="reset" onClick={reset}> |
| 重置 |
| </Button> |
| </Form.Item> |
开始升级版本(动态渲染搜索框)
接下来可以将搜索的数据改为动态渲染 因为按钮可以固定了 值从父级传入
| {props.formList.map((item: SearchFormItem) => ( |
| <Form.Item |
| label={props.showLabel !== false && item.label ? item.label : ''} |
| key={item.name} |
| name={item.name} |
| rules={item.rules} |
| style={{ width: `${item.width}px` }} |
| > |
| {item.render ? item.render : <Input placeholder={item.placeholder} />} |
| </Form.Item> |
| ))} |
继续升级(方法通过子传父)
| function SearchForm(props: SearchFormProps) { |
| const [form] = Form.useForm(); |
| |
| const reset = () => { |
| form.resetFields(); |
| props.onSearch({}); |
| }; |
| |
| const onSearch = () => { |
| form.validateFields().then(res => { |
| props.onSearch(res); |
| }); |
| }; |
| |
| return ( |
| <Form className="layout__search" form={form} layout="inline" onFinish={onSearch}> |
| {props.formList.map((item: SearchFormItem) => ( |
| <Form.Item |
| label={props.showLabel !== false && item.label ? item.label : ''} |
| key={item.name} |
| name={item.name} |
| rules={item.rules} |
| style={{ width: `${item.width}px` }} |
| > |
| {item.render ? item.render : <Input placeholder={item.placeholder} />} |
| </Form.Item> |
| ))} |
| <Form.Item> |
| <Button htmlType="submit">查询</Button> |
| </Form.Item> |
| |
| <Form.Item> |
| <Button htmlType="reset" onClick={reset}> |
| 重置 |
| </Button> |
| </Form.Item> |
| { |
| props.actions.map((action: SearchFormAction, index: number) => ( |
| <Form.Item key={action.name}> |
| <Button type={action.type} onClick={() => props.onClick(index)}> |
| {action.name} |
| </Button> |
| </Form.Item> |
| )) |
| } |
| </Form > |
| ); |
| } |
| |
继续升级(ts限定数据类型)
| import React, { memo } from 'react'; |
| import { Button, Input, Form } from 'antd'; |
| import { ButtonType } from 'antd/es/button/button'; |
| import './index.less'; |
| |
| export interface SearchFormAction { |
| name: string; |
| type?: ButtonType; |
| } |
| |
| export interface SearchFormItem { |
| name: string; |
| label: string; |
| placeholder?: string; |
| rules?: object[]; |
| render?: React.ReactElement; |
| width?: any |
| } |
| |
| interface SearchFormProps { |
| formList: SearchFormItem[]; |
| onSearch: (values: any) => void; |
| actions: SearchFormAction[]; |
| onClick: (index: number) => void; |
| showLabel?: boolean; |
| width?: any |
| } |
| |
| function SearchForm(props: SearchFormProps) { |
| const [form] = Form.useForm(); |
| |
| const reset = () => { |
| form.resetFields(); |
| props.onSearch({}); |
| }; |
| |
| const onSearch = () => { |
| form.validateFields().then(res => { |
| props.onSearch(res); |
| }); |
| }; |
| |
| return ( |
| <Form className="layout__search" form={form} layout="inline" onFinish={onSearch}> |
| {props.formList.map((item: SearchFormItem) => ( |
| <Form.Item |
| label={props.showLabel !== false && item.label ? item.label : ''} |
| key={item.name} |
| name={item.name} |
| rules={item.rules} |
| style={{ width: `${item.width}px` }} |
| > |
| {item.render ? item.render : <Input placeholder={item.placeholder} />} |
| </Form.Item> |
| ))} |
| <Form.Item> |
| <Button htmlType="submit">查询</Button> |
| </Form.Item> |
| |
| <Form.Item> |
| <Button htmlType="reset" onClick={reset}> |
| 重置 |
| </Button> |
| </Form.Item> |
| { |
| props.actions.map((action: SearchFormAction, index: number) => ( |
| <Form.Item key={action.name}> |
| <Button type={action.type} onClick={() => props.onClick(index)}> |
| {action.name} |
| </Button> |
| </Form.Item> |
| )) |
| } |
| </Form > |
| ); |
| } |
| |
| export default memo(SearchForm); |
| |
| |
看看父组件的使用
| <Card> |
| <SearchForm |
| formList={formList} |
| actions={actions} |
| onSearch={onSearch} |
| onClick={onAddMenu} |
| showLabel={true} |
| ></SearchForm> |
| </Card> |
formList搜索表单值
| const formList = useMemo<SearchFormItem[]>( |
| () => [ |
| { |
| width: 280, |
| name: 'factoryId', |
| placeholder: '请选择所属工厂', |
| label: '所属工厂', |
| render: ( |
| <Select |
| style={{ width: '100%' }} |
| placeholder="请选择所属工厂" |
| optionFilterProp="children" |
| > |
| {factoryDataList && factoryDataList.map((item: any) => ( |
| <Option value={item.id}>{item.name}</Option> |
| ))} |
| </Select> |
| ) |
| }, |
| ], |
| [], |
| ); |
actions按钮值
| const actions = useMemo<SearchFormAction[]>( |
| () => [ |
| { |
| name: '新建', |
| type: 'primary', |
| }, |
| ], |
| [], |
| ); |
onSearch子传父方法值
| const onSearch = useCallback( |
| (params: MenuSearchParams) => { |
| initPageList(params); |
| }, |
| [page], |
| ); |
onAddMenu 控制弹框开启的值
| const onAddMenu = useCallback(() => { |
| setCurrentMenu(null); |
| setEditVisible(true); |
| }, []); |
结果

我是歌谣 放弃很容易 坚持一定很酷 关注前端小歌谣带你进入前端巅峰交流群
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南