React Table Trigger Changed Without SWR

I'm into the world of javascript and reactjs is absolutely nil! And I found react-table from TanStack and it's really cool! That agnostic (What The Buff!)

And I'm trying to simplify my old way of working with jQuery when I was almost sinking to the bottom of the ocean (Hypertext Preprocessor) and didn't find the light of JavaScript as a complete combat tool more promising.

In jQuery I need to create a function to repeat the request and I trigger it from the targeted event and it's easy.

My question is how can I do the same thing but in react-table by not using any other library.

And here's what happened:

// file: components/TablePagination.js
function TablePagination({
    columns,
    data,
    fetchData,
    loading,
    pageCount: controlledPageCount,
    totalRow,
    actions: Actions
}) {
    const {
        getTableProps,
        getTableBodyProps,
        headerGroups,
        prepareRow,
        page,
        canPreviousPage,
        canNextPage,
        pageOptions,
        pageCount,
        gotoPage,
        nextPage,
        previousPage,
        setPageSize,
        state: { pageIndex, pageSize, globalFilter, sortBy },
        preGlobalFilteredRows,
        setGlobalFilter
    } = useTable(
        {
            columns,
            data,
            manualPagination: true,
            manualGlobalFilter: true,
            manualSortBy: true,
            initialState: {
                pageIndex: 0,
                pageSize: 10
            }, // Pass our hoisted table state
            pageCount: controlledPageCount,
            autoResetSortBy: false,
            autoResetExpanded: false,
            autoResetPage: false
        },
        useGlobalFilter,
        useSortBy,
        usePagination
    );

    const GlobalFilter = ({ preGlobalFilteredRows, globalFilter, setGlobalFilter }) => {
        const count = preGlobalFilteredRows
        const [value, setValue] = React.useState(globalFilter)
        const onChange = useAsyncDebounce(value => {
            setGlobalFilter(value || undefined)
        }, 700)

        return (
            <div className={Actions !== undefined ? 'flex flex-row justify-between' : 'flex flex-col'}>
                {Actions !== undefined ? (<Actions />) : null}
                <input
                    value={value || ""}
                    onChange={e => {
                        setValue(e.target.value);
                        onChange(e.target.value);
                    }}
                    placeholder={`${count} records...`}
                    type="search"
                    className={`input input-bordered input-sm w-full max-w-xs focus:outline-0 mb-2 ${Actions !== undefined ? '' : 'self-end'}`}
                />
            </div>
        )
    }

    React.useEffect(() => {
        let search = globalFilter === undefined ? '' : globalFilter
        fetchData(pageSize, pageIndex, search);
    }, [fetchData, pageIndex, pageSize, globalFilter, sortBy]);

    return (
        <>
            <GlobalFilter
                preGlobalFilteredRows={totalRow}
                globalFilter={globalFilter}
                setGlobalFilter={setGlobalFilter}
            />
            <div className="overflow-x-auto">
                <table {...getTableProps()} className='table table-compact table-zebra w-full'>
                    <thead>
                        {headerGroups.map(headerGroup => (
                            <tr {...headerGroup.getHeaderGroupProps()}>
                                {headerGroup.headers.map(column => (
                                    <th {...column.getHeaderProps(column.getSortByToggleProps())}>
                                        <span>
                                            {column.isSorted ? column.isSortedDesc ? <ArrowLongDownIcon className='h-4 w-4 inline mr-1' /> : <ArrowLongUpIcon className='h-4 w-4 inline mr-1' /> : <FunnelIcon className='h-4 w-4 inline mr-1' />}
                                        </span>
                                        {column.render('Header')}
                                    </th>
                                ))}
                            </tr>
                        ))}
                    </thead>
                    <tbody {...getTableBodyProps()}>
                        {page.length > 0 ? page.map((row, i) => {
                            prepareRow(row)
                            return (
                                <tr {...row.getRowProps()} className='hover'>
                                    {row.cells.map(cell => {
                                        return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
                                    })}
                                </tr>
                            )
                        }) : (<tr className='hover'>
                            <td colSpan={10000} className='text-center'>Data not found!</td>
                        </tr>)}
                        {loading ? (
                            <tr>
                                <td colSpan="10000">Loading...</td>
                            </tr>
                        ) : null}
                    </tbody>
                </table>
            </div>
            <div className="flex flex-row justify-between">
                <div className='mt-2'>
                    <span>
                        Halaman{' '}
                        <strong>
                            {pageIndex + 1} dari {pageOptions.length}
                        </strong>{' '}
                        Total <strong>{preGlobalFilteredRows.length}</strong>{' '}
                    </span>
                    <span>
                        | Lompat ke halaman:{' '}
                        <input
                            type="number"
                            defaultValue={pageIndex + 1}
                            onChange={e => {
                                const page = e.target.value ? Number(e.target.value) - 1 : 0
                                gotoPage(page)
                            }}
                            className="input input-bordered input-sm w-20 max-w-xs focus:outline-0"
                        />
                    </span>{' '}
                    <select
                        value={pageSize}
                        onChange={e => {
                            setPageSize(Number(e.target.value))
                        }}
                        className="select select-bordered select-sm w-30 max-w-xs focus:outline-0"
                    >
                        {[10, 20, 30, 40, 50].map(pageSize => (
                            <option key={pageSize} value={pageSize}>
                                Tampilkan {pageSize} baris
                            </option>
                        ))}
                    </select>
                </div>
                <div className='mt-2'>
                    <button className='btn btn-xs' onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
                        {'<<'}
                    </button>{' '}
                    <button className='btn btn-xs' onClick={() => previousPage()} disabled={!canPreviousPage}>
                        {'<'}
                    </button>{' '}
                    <button className='btn btn-xs' onClick={() => nextPage()} disabled={!canNextPage}>
                        {'>'}
                    </button>{' '}
                    <button className='btn btn-xs' onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
                        {'>>'}
                    </button>{' '}
                </div>
            </div>
        </>
    );
}

export default TablePagination

What I really want is that when I update data from a modal component (child), I can trigger the previous component (parent) I have to refresh the data in the table after a data change.

// file: pages/Example.js (parent)
function Example() {

    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(false)
    const [pageCount, setPageCount] = useState(0)
    const [totalRow, setTotalRow] = useState(0)
    const [refresh, setRefresh] = useState(false)

    const fetchData = useCallback(async (pageSize, pageIndex, search) => {
        setLoading(true)
        const queryOptions = {
            page: pageIndex,
            limit: pageSize,
            search: search
        }
        const customers = await customerDatatable(queryOptions)

        setData(customers.data)
        setPageCount(customers.pagination.totalPage)
        setTotalRow(customers.pagination.totalRow)
        setLoading(false)
        setRefresh(false)
    }, [refresh]);

    const columns = useMemo(
        () => [
            ....,
            {
                Header: 'Actions',
                accessor: (row) => {
                    return (
                        <div className='flex flex-row w-38'>
                            <ReuseableModal modalId={`modalEdit-${row.customer_uuid}`} btnClassName={`btn-xs btn-info mr-2`} btnContent={<PencilSquareIcon className='h-3 w-3' />} width='w-11/12 max-w-5xl'>
                                // here the child
                                <FormEdit data={row} setRefresh={setRefresh} modalTarget={row.customer_uuid} />
                            </ReuseableModal>
                        </div>
                    )
                },
                disableSortBy: true
            }
        ],
        []
    );

    return (
        <Fragment>
            <Helmet>
                <title>Example</title>
            </Helmet>
            <section className='p-3'>
                <div className="bg-base-300 p-3 rounded">
                    <TablePagination
                        columns={columns}
                        data={data}
                        fetchData={fetchData}
                        loading={loading}
                        pageCount={pageCount}
                        totalRow={totalRow}
                    />
                </div>
            </section>
        </Fragment>
    )
}

export default PelangganAktif
}

And here the modal popup

// file: components/modal/FormEdit.js (child)
function FormEdit({ data, setRefresh, modalTarget }) {

    const { addToast } = useToast()
    const initValues = data

    const formSubmit = async (values) => {
        const updated = await customerUpdate(values)
        if (updated.type === 'success') {
            addToast('success', 'top-right', 'Data updated!', `${data.profiles.fullname} detail updated`, 5000)
            document.getElementById(`modalEdit-${modalTarget}`).click()
            setRefresh(true)
            resetForm()
        } else {
            addToast('error', 'top-right', 'Data failed to update!', `${data.profiles.fullname} defail failed to update`, 5000)
        }
    }

    const { values, errors, touched, handleChange, handleSubmit, resetForm } = useFormik({
        initialValues: initValues,
        onSubmit: formSubmit,
        enableReinitialize: true
    })

    return // your form here
}

export default FormEdit

That's it! No Joke!

1
Subscribe to my newsletter

Read articles from Imam Ali Mustofa directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Imam Ali Mustofa
Imam Ali Mustofa

Software Freestyle Engineer (Coding while Dancing) 🐧 Cinnamon + 🚬 Cigarettes = ⌨️ Metaphor Story