Using ref with React Hooks in Ant design (antd) Table component with customized filters
Asked Answered
D

5

13

I'm trying to use Antd Table https://ant.design/components/table/ with customized filters and React Hooks. Most of all code from provided example were converted to hooks' logic successfully but I have no idea how to convert "this.searchInput" (which are commented below) from the code.

Example from the Antd site which was converted by me to React Hooks:

import { Table, Input, Button, Space } from 'antd';
import Highlighter from 'react-highlight-words';
import { SearchOutlined } from '@ant-design/icons';



function App() {
  const [searchText, setSearchText] = useState('');
  const [searchedColumn, setSearchedColumn] = useState('');

  const data = [
    {
      key: '1',
      name: 'John Brown',
      age: 32,
      address: 'New York No. 1 Lake Park',
    },
    {
      key: '2',
      name: 'Joe Black',
      age: 42,
      address: 'London No. 1 Lake Park',
    },
    {
      key: '3',
      name: 'Jim Green',
      age: 32,
      address: 'Sidney No. 1 Lake Park',
    },
    {
      key: '4',
      name: 'Jim Red',
      age: 32,
      address: 'London No. 2 Lake Park',
    },
  ];

  function getColumnSearchProps(dataIndex) {
    return {
      filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
        <div style={{ padding: 8 }}>
          <Input
            // ref={node => {
            //   this.searchInput = node;
            // }}
            placeholder={`Search ${dataIndex}`}
            value={selectedKeys[0]}
            onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
            onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
            style={{ width: 188, marginBottom: 8, display: 'block' }}
          />
          <Space>
            <Button
              type="primary"
              onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
              icon={<SearchOutlined />}
              size="small"
              style={{ width: 90 }}
            >
              Search
            </Button>
            <Button onClick={() => handleReset(clearFilters)} size="small" style={{ width: 90 }}>
              Reset
            </Button>
          </Space>
        </div>
      ),
      filterIcon: filtered => <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />,
      onFilter: (value, record) =>
        record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()),
      onFilterDropdownVisibleChange: visible => {
        if (visible) {
          // setTimeout(() => this.searchInput.select());
        }
      },
      render: text =>
        searchedColumn === dataIndex ? (
          <Highlighter
            highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
            searchWords={[searchText]}
            autoEscape
            textToHighlight={text.toString()}
          />
        ) : (
          text
        ),
    }
  };

  function handleSearch(selectedKeys, confirm, dataIndex) {
    confirm();
    setSearchText(selectedKeys[0]);
    setSearchedColumn(dataIndex);
  };

  function handleReset(clearFilters) {
    clearFilters();
    setSearchText('');
  };

  const columns = [
    {
      title: 'Name',
      dataIndex: 'name',
      key: 'name',
      width: '30%',
      ...getColumnSearchProps('name'),
    },
    {
      title: 'Age',
      dataIndex: 'age',
      key: 'age',
      width: '20%',
      ...getColumnSearchProps('age'),
    },
    {
      title: 'Address',
      dataIndex: 'address',
      key: 'address',
      ...getColumnSearchProps('address'),
    },
  ];

  return <Table columns={columns} dataSource={data} />;

}

export default App;

Thank you.

Duckett answered 11/5, 2020 at 19:46 Comment(1)
hey, I'm using same antd table react hooks syntax just like you, but for me setSearchText and setSearchedColumn is not updating the state for some reason. Are you facing the same issue? If I log state after set, it still has no value.Cunningham
E
13

Try next:

function App() {
  const [searchText, setSearchText] = useState('');
  const [searchedColumn, setSearchedColumn] = useState('');
  const searchInput = useRef(null);
  ...
    <Input
      ref={ searchInput }
  ...
Enaenable answered 11/5, 2020 at 20:36 Comment(1)
Thank you Aleksey. It works. For the second case, I used this code: if (visible) { setTimeout(() => searchInput.current.select()); }Duckett
J
6

For the versions:

  • react 18.1.0
  • antd: 4.20.2

the following needs to be done for TypeScript (for JavaScript just remove the word "type"):

import { Input } from 'antd'
import type { InputRef } from 'antd'

const someInputElementRef = useRef<InputRef>(null)
Jamestown answered 27/5, 2022 at 14:19 Comment(0)
C
1

You can create ref using useRef hook for your functional compoent:

const searchInput = useRef<Input>(null)

and pass it to Input:

<Input
  ref={searchInput}
  placeholder={`Search ${dataIndex}`}
  {...restProps}
/>

and to auto select it when opened:

onFilterDropdownVisibleChange: (visible) => {
  if (visible) {
    setTimeout(
      () => searchInput && searchInput.current && searchInput.current.select()
    )
    // null check above: as its initial value was null
  }
},

For earlier release (before 16.3) of React, callback-ref was the recommended way to create ref which has been used in the example you mentioned (unlike we used above):

<Input
  ref={node => {
     this.searchInput = node;
  }}
  placeholder={`Search ${dataIndex}`}
  {...restProps}
/>
Cocksure answered 11/5, 2020 at 21:33 Comment(2)
Thank you Ajeet.Duckett
Confirm that this works with current AntD version.. ThanksReturnable
Y
0

For antd version 4.24.1 and React version 18.2.0:
This works fine.

    function App() {
      const searchInput = useRef(null);

      const handleInput = () => {
        console.log(searchInput.current.input.value)
      }

      // Other Code
        <Input
          ref={ searchInput }
          // other props
        />
      /// Other Code
Yarndyed answered 25/11, 2022 at 14:6 Comment(0)
S
-1

I tried via custom hook but it didn't work:

import React, { useState, useRef } from 'react'
import { Button, Space, Input } from 'antd'
import { SearchOutlined } from '@ant-design/icons'
import Highlighter from 'react-highlight-words'

export default function useColumnSearch({ dataIndexExt }) {
    const [searchText, setSearchText] = useState('')
    const [searchedColumn, setSearchedColumn] = useState(0)
    const refSearchInput = useRef()

    const getColumnSearchProps = (dataIndex) => ({
        filterDropdown: ({
            setSelectedKeys,
            selectedKeys,
            confirm,
            clearFilters
        }) => (
                // placeholder={`Search ${dataIndex}`}
                <div style={{ padding: 8 }}>
                    <Input
                        ref={refSearchInput}
                        value={selectedKeys[0]}
                        onChange={(e) =>
                            setSelectedKeys(e.target.value ? [e.target.value] : [])
                        }
                        onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
                        style={{ width: 188, marginBottom: 8, display: "block" }}
                    />
                    <Space>
                        <Button
                            type="primary"
                            onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
                            icon={<SearchOutlined />}
                            size="small"
                            style={{ width: 90 }}
                        >
                            Search
                        </Button>
                        <Button
                            onClick={() => handleReset(clearFilters)}
                            size="small"
                            style={{ width: 90 }}
                        >
                            Reset
                        </Button>
                    </Space>
                </div>
            ),
        filterIcon: (filtered) => (
            <SearchOutlined style={{ color: filtered ? '#1890ff' : undefined }} />
        ),
        onFilter: (value, record) =>
            record[dataIndex]
                ? record[dataIndex]
                    .toString()
                    .toLowerCase()
                    .includes(value.toLowerCase())
                : '',
        onFilterDropdownVisibleChange: (visible) => {
            if (visible) {
                setTimeout(() => refSearchInput.current.select(), 100)
            }
        },
        render: (text) =>
            searchedColumn === dataIndex ? (
                <Highlighter
                    highlightStyle={{ backgroundColor: '#ffc069', padding: 0 }}
                    searchWords={[searchText]}
                    autoEscape
                    textToHighlight={text ? text.toString() : ''}
                />
            ) : (
                    text
                )
    })

    const handleSearch = (selectedKeys, confirm, dataIndex) => {
        confirm()
        setSearchText(selectedKeys[0])
        setSearchedColumn(dataIndex)
    }

    const handleReset = (clearFilters) => {
        clearFilters()
        setSearchText('')
    }

    return getColumnSearchProps(dataIndexExt)
}

my example in continuation of Aleksey Polonka's answer:

codesandbox ant design Table with custom filter

Sailfish answered 14/12, 2020 at 11:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.