3

How to override the global filter matching function in react-table by tanstack?

I should like to add support for exact matching using ". For example when searching for "male" that should not include rows contain "female" rows(Similar to how it works on google).

I have a idea to create something like this (Not tested):

function getRandomBoolean = (filter, row) => {
  const searchTerm = filter.value.trim();
  if (searchTerm.startsWith('"') && searchTerm.endsWith('"')) {
    // Perform an exact match search if the search term is enclosed in quotes
    const exactSearchTerm = searchTerm.slice(1, -1);
    return row === exactSearchTerm;
  } else {
    // Perform a regular search if no quotes are used
    return row.toLowerCase().includes(searchTerm.toLowerCase());
  }
}

However not sure how to override the default global filter function. Is it possible?

Or is there some other settings to get this type of search easier?

I have tried to look here but think the documentation is hard to read: https://react-table-v7.tanstack.com/docs/api/useGlobalFilter

I have this sampel code that is based on this tutorial: https://www.youtube.com/watch?v=GsDP1bLoIQU&list=PLC3y8-rFHvwgWTSrDiwmUsl4ZvipOw9Cz&index=8

This is a working example for the global search (I put everything in same file to make it easier to test in a new project). I assume there might just be one place to override it. Anyone knows where or understand the documentation how to do it?

import React, { useMemo } from 'react'

import { useTable, useGlobalFilter } from 'react-table'
//import { COLUMNS } from './columns'
//import MOCK_DATA from './MOCK_DATA.json'
const COLUMNS = [
    { Header: 'id', accessor: 'id' },
    { Header: 'First name', accessor: 'first_name' },
    { Header: 'Last name', accessor: 'last_name' },
    { Header: 'Mail', accessor: 'email' },
    { Header: 'gender', accessor: 'gender' },
    { Header: 'age', accessor: 'age' },
    { Header: 'country', accessor: 'country' },
    { Header: 'Phone', accessor: 'phone' }
];
const MOCK_DATA =
[{"id":1,"first_name":"Abey","last_name":"Shattock","email":"ashattock0@who.int","gender":"male","age":60,"country":"Sweden","phone":"3235563967"},
{"id":2,"first_name":"Gert","last_name":"Verey","email":"gverey1@miitbeian.gov.cn","gender":"male","age":39,"country":"Argentina","phone":"6617228260"},
{"id":3,"first_name":"Elsa","last_name":"McDermott-Row","email":"gmcdermottrow2@fc2.com","gender":"female","age":73,"country":"China","phone":"5049019528"},
{"id":4,"first_name":"Ade","last_name":"Southwick","email":"asouthwick3@gmpg.org","gender":"male","age":58,"country":"France","phone":"2562514369"},
{"id":5,"first_name":"Angelina","last_name":"Almond","email":"aalmond4@ezinearticles.com","gender":"Female","age":30,"country":"Paraguay","phone":"9509201868"},
]
export const GlobalFilter = ({filter, setFilter}) => {
    return (
        <span>
            Search: {' '} 
            <input value={filter || ''} onChange={(e) => setFilter(e.target.value)} />
        </span>
    )
}

export const BasicTable = () => {

   const columns = useMemo( () => COLUMNS, []);
   const data = useMemo( () => MOCK_DATA, []);

    const tableInstance = useTable({
            columns: columns,
            data: data
        },
        useGlobalFilter
    )

    const {getTableProps, getTableBodyProps, prepareRow, state, setGlobalFilter, headerGroups, rows } = tableInstance

   const  { globalFilter } = state
    return (
        <div>
            <GlobalFilter filter={globalFilter} setFilter={setGlobalFilter}/>
            <table { ...getTableProps() }>
                <thead>
                    {
                        headerGroups.map( (headerGroup) => (
                            <tr {...headerGroup.getHeaderGroupProps()}>
                                {
                                    headerGroup.headers.map(( column ) => (
                                        <th {...column.getHeaderProps()}>{column.render('Header')}</th>
                                    ))
                                }
                            </tr>
                        ))
                    }
                </thead>
                <tbody { ...getTableBodyProps() }>
                    
                        {rows.map( (row) => {
                            prepareRow(row)
                            return (
                                <tr {...row.getRowProps()}>
                                    {
                                        row.cells.map((cell) => {
                                            return <td {...cell.getCellProps()}> {cell.render('Cell')}</td>
                                        })
                                    }
                                </tr>
                            )
                        })
                        }

                </tbody>
            </table>
        </div>
    )
}
starking
  • 153
  • 10
  • 1
    If you want more control over the filters, you can implement your own logic and change `data` to only contain the things you'd like. – 0stone0 Mar 27 '23 at 14:15
  • No I want to do it using the global search to filter the data. Not to change the original data after it has been fetched. – starking Mar 27 '23 at 14:16
  • I think it should be solved adding additional filter types to 'filterTypes' attribute on the table. Then you inform the columns to use or set it defaultFilter for all columns. https://react-table-v7.tanstack.com/docs/api/useFilters#table-options But I can not figure out how to do it even after investigation. – starking Mar 27 '23 at 16:18

1 Answers1

2

Here is how you can override the globalFilter and do you "exact match".

It ignore when only one " has been entered first and not the matching in the end has been entered.

import React, { useMemo } from 'react'


import { useTable, useGlobalFilter } from 'react-table'

const COLUMNS = [
    { Header: 'id', accessor: 'id' },
    { Header: 'First name', accessor: 'first_name' },
    { Header: 'Last name', accessor: 'last_name' },
    { Header: 'Mail', accessor: 'email' },
    { Header: 'gender', accessor: 'gender' },
    { Header: 'age', accessor: 'age' },
    { Header: 'country', accessor: 'country' },
    { Header: 'Phone', accessor: 'phone' }
];
const MOCK_DATA =
[{"id":1,"first_name":"Abey","last_name":"Shattock","email":"ashattock0@who.int","gender":"male","age":60,"country":"Sweden","phone":"3235563967"},
{"id":2,"first_name":"Gert","last_name":"Verey","email":"gverey1@miitbeian.gov.cn","gender":"male","age":39,"country":"Argentina","phone":"6617228260"},
{"id":3,"first_name":"Elsa","last_name":"McDermott-Row","email":"gmcdermottrow2@fc2.com","gender":"female","age":73,"country":"China","phone":"5049019528"},
{"id":4,"first_name":"Ade","last_name":"Southwick","email":"asouthwick3@gmpg.org","gender":"male","age":58,"country":"France","phone":"2562514369"},
{"id":5,"first_name":"Angelina","last_name":"Almond","email":"aalmond4@ezinearticles.com","gender":"Female","age":30,"country":"Paraguay","phone":"9509201868"},
]

// Remove the value here in input else you get a warning
export const GlobalFilter = ({filter, setFilter}) => {
    return (
        <span>
            Search: {' '} 
            <input  onChange={(e) => setFilter(e.target.value)} />
        </span>
    )
}

export const BasicTable = () => {
   
    // You need to define your global filter here
    const globalFilter = React.useCallback((rows, ids, query) => {
        const searchTerm = String(query);

        return rows.filter((row) => {
            const matches = ids.filter((id) => {
                const rowValue = row.values[id];
                let searchTermLower = searchTerm.toLowerCase();

                if (searchTerm == '"')  {
                    // ignore first ' " ' handle it as no input
                    return true
                }
                else if (searchTerm.startsWith('"') && searchTerm.endsWith('"')) {
                    const exactSearchTerm = searchTerm.slice(1, -1);
                    return String(rowValue) === exactSearchTerm;
                }
                else if (searchTerm.startsWith('"'))  {
                    // ignore until ending ' " ' has been entered
                    searchTermLower = searchTerm.slice(1);
                }
   
                return rowValue !== undefined
                    ? String(rowValue).toLowerCase().includes(searchTermLower.toLowerCase())
                    : false;
            });

            return matches.length > 0;
        });
    }, []); 


   const columns = useMemo( () => COLUMNS, []);
   const data = useMemo( () => MOCK_DATA, []);

    const tableInstance = useTable({
            columns: columns,
            data: data,
            globalFilter: globalFilter // send it in here
        },
        useGlobalFilter
    )

    const { getTableProps, getTableBodyProps, prepareRow, setGlobalFilter, headerGroups, rows } = tableInstance

    return (
        <div>
            <GlobalFilter filter={globalFilter} setFilter={setGlobalFilter}/>
            <table { ...getTableProps() }>
                <thead>
                    {
                        headerGroups.map( (headerGroup) => (
                            <tr {...headerGroup.getHeaderGroupProps()}>
                                {
                                    headerGroup.headers.map(( column ) => (
                                        <th {...column.getHeaderProps()}>{column.render('Header')}</th>
                                    ))
                                }
                            </tr>
                        ))
                    }
                </thead>
                <tbody { ...getTableBodyProps() }>
                    
                        {rows.map( (row) => {
                            prepareRow(row)
                            return (
                                <tr {...row.getRowProps()}>
                                    {
                                        row.cells.map((cell) => {
                                            return <td {...cell.getCellProps()}> {cell.render('Cell')}</td>
                                        })
                                    }
                                </tr>
                            )
                        })
                        }

                </tbody>
            </table>
        </div>
    )
}
karin holm
  • 104
  • 4