如何将React函数组件转为AgGrid要求的new() => any类型?
AgGridReact 自定义函数组件过滤器类型不匹配问题解决
问题场景
在给AgGridReact实现自定义过滤器时,无论是将函数组件赋值给frameworkComponents:
frameworkComponents: { newcustomFilter: CustomFilter },
还是直接在columnDefs中指定filter: CustomFilter,都会触发相同的TypeScript错误:
Type '(props: any) => JSX.Element' is not assignable to type 'new () => any'. Type '(props: any) => Element' provides no match for the signature 'new (): any'.ts(2322)
我的自定义过滤器是React函数组件,代码如下:
import React from 'react' import { useEffect, useState } from "react"; const CustomFilter = (props:any):any => { const [filterState, setFilterState] = useState('off'); useEffect( ()=> { console.log(props.title + ' filter created'); return ()=> console.log(props.title + ' filter destroyed'); }, []); useEffect( ()=> { props.filterChangedCallback() }, [filterState]); const handleChange = (e:any) => { setFilterState(e.target.value) } const isFilterActive = () => { return filterState!='off'; } const doesFilterPass = (params:any) => { const field = props.colDef.field; return params.data[field] == filterState; } const getModel =() => { if (filterState=='off') { return undefined; } return { state: filterState } } const setModel = (model:any) => { if (model==null) { setFilterState('off'); } else { setFilterState(model.state); } } const getModelAsString = () => { return filterState=='off' ? '' : filterState; } const onNewRowsLoaded = () => { console.log('new rows were loaded'); } const onAnyFilterChanged = () => { console.log('another filter was changed'); } const afterGuiAttached = () => { console.log('focus something???'); } return ( <div> <div className='filter-title'>{props.title}</div> <div className='filter-state'>State = {filterState}</div> <div className='filter-entry'> <button onClick={()=>setFilterState('off')}>Off</button> </div> <select onChange={handleChange}> {props.values.map((option:any) => ( <option key={option.value} value={option.value}> {option.text} </option> ))} </select> </div> ); }
问题原因
AgGrid的自定义过滤器要求组件是可实例化的构造函数类型(即类组件,符合new() => any签名),而React函数组件是无状态的函数,不具备构造函数特性,因此类型不匹配。
解决方案
方案1:用forwardRef + useImperativeHandle适配函数组件(推荐)
通过React的forwardRef和useImperativeHandle,将AgGrid所需的过滤器方法暴露给Grid实例,同时保留函数组件的写法:
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; import { IFilterComp, IFilterParams } from 'ag-grid-community'; // 使用forwardRef让AgGrid能获取到组件实例的方法 const CustomFilter = forwardRef<IFilterComp, IFilterParams>((props, ref) => { const [filterState, setFilterState] = useState('off'); // 暴露IFilterComp接口要求的所有方法给AgGrid useImperativeHandle(ref, () => ({ isFilterActive: () => filterState !== 'off', doesFilterPass: (params: any) => { const field = props.colDef.field; return params.data[field] === filterState; }, getModel: () => filterState === 'off' ? undefined : { state: filterState }, setModel: (model: any) => { setFilterState(model ? model.state : 'off'); }, getModelAsString: () => filterState === 'off' ? '' : filterState, onNewRowsLoaded: () => console.log('new rows were loaded'), onAnyFilterChanged: () => console.log('another filter was changed'), afterGuiAttached: () => console.log('focus something???') })); useEffect(() => { console.log(props.title + ' filter created'); return () => console.log(props.title + ' filter destroyed'); }, []); useEffect(() => { props.filterChangedCallback(); }, [filterState]); const handleChange = (e: any) => { setFilterState(e.target.value); }; return ( <div> <div className='filter-title'>{props.title}</div> <div className='filter-state'>State = {filterState}</div> <div className='filter-entry'> <button onClick={() => setFilterState('off')}>Off</button> </div> <select onChange={handleChange}> {props.values.map((option: any) => ( <option key={option.value} value={option.value}> {option.text} </option> ))} </select> </div> ); }); // 设置displayName方便调试 CustomFilter.displayName = 'CustomFilter';
使用方式
- 在Grid的
frameworkComponents中注册组件:
frameworkComponents: { customFilter: CustomFilter },
- 在
columnDefs中指定使用该过滤器(注意用注册的字符串名称,而非组件本身):
columnDefs: [ { headerName: 'PRICE', field: WATERFALL_COLUMN_IDS.PRICE, colId: WATERFALL_COLUMN_IDS.PRICE, minWidth: 70, width: 92, hide: false, filter: 'customFilter', // 对应frameworkComponents中的键名 filterParams: { title: 'Year Filter', values: [2000,2004,2006] }, } ]
方案2:将函数组件转为类组件
如果更习惯类组件写法,可以直接将函数组件重构为实现IFilterComp接口的类组件:
import React, { Component } from 'react'; import { IFilterComp, IFilterParams } from 'ag-grid-community'; class CustomFilter extends Component<IFilterParams, { filterState: string }> implements IFilterComp { constructor(props: IFilterParams) { super(props); this.state = { filterState: 'off' }; } isFilterActive(): boolean { return this.state.filterState !== 'off'; } doesFilterPass(params: any): boolean { const field = this.props.colDef.field; return params.data[field] === this.state.filterState; } getModel(): any { return this.state.filterState === 'off' ? undefined : { state: this.state.filterState }; } setModel(model: any): void { this.setState({ filterState: model ? model.state : 'off' }); } getModelAsString(): string { return this.state.filterState === 'off' ? '' : this.state.filterState; } onNewRowsLoaded(): void { console.log('new rows were loaded'); } onAnyFilterChanged(): void { console.log('another filter was changed'); } afterGuiAttached(): void { console.log('focus something???'); } componentDidMount(): void { console.log(this.props.title + ' filter created'); } componentWillUnmount(): void { console.log(this.props.title + ' filter destroyed'); } componentDidUpdate(prevProps: IFilterParams, prevState: { filterState: string }): void { if (prevState.filterState !== this.state.filterState) { this.props.filterChangedCallback(); } } handleChange = (e: any) => { this.setState({ filterState: e.target.value }); } render() { const { filterState } = this.state; const { title, values } = this.props; return ( <div> <div className='filter-title'>{title}</div> <div className='filter-state'>State = {filterState}</div> <div className='filter-entry'> <button onClick={() => this.setState({ filterState: 'off' })}>Off</button> </div> <select onChange={this.handleChange}> {values.map((option: any) => ( <option key={option.value} value={option.value}> {option.text} </option> ))} </select> </div> ); } }
使用方式
直接在columnDefs中指定组件即可:
columnDefs: [ { // ...其他配置 filter: CustomFilter, filterParams: { title: 'Year Filter', values: [2000,2004,2006] }, } ]
内容的提问来源于stack exchange,提问作者Raj
相关产品推荐
相关产品推荐

