.darkterminal

React Table Trigger Changed Without SWR

2022-01-26 - - - - - Metaphor

Originally Post at: Metaphore - SCP and Issue on GitHub Metaphore Repository #8

The Back Story about this Metaphor

React Table Trigger Changed Without SWR

Metaphore story

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:

1// file: components/TablePagination.js
2function TablePagination({
3    columns,
4    data,
5    fetchData,
6    loading,
7    pageCount: controlledPageCount,
8    totalRow,
9    actions: Actions
10}) {
11    const {
12        getTableProps,
13        getTableBodyProps,
14        headerGroups,
15        prepareRow,
16        page,
17        canPreviousPage,
18        canNextPage,
19        pageOptions,
20        pageCount,
21        gotoPage,
22        nextPage,
23        previousPage,
24        setPageSize,
25        state: { pageIndex, pageSize, globalFilter, sortBy },
26        preGlobalFilteredRows,
27        setGlobalFilter
28    } = useTable(
29        {
30            columns,
31            data,
32            manualPagination: true,
33            manualGlobalFilter: true,
34            manualSortBy: true,
35            initialState: {
36                pageIndex: 0,
37                pageSize: 10
38            }, // Pass our hoisted table state
39            pageCount: controlledPageCount,
40            autoResetSortBy: false,
41            autoResetExpanded: false,
42            autoResetPage: false
43        },
44        useGlobalFilter,
45        useSortBy,
46        usePagination
47    );
48
49    const GlobalFilter = ({ preGlobalFilteredRows, globalFilter, setGlobalFilter }) => {
50        const count = preGlobalFilteredRows
51        const [value, setValue] = React.useState(globalFilter)
52        const onChange = useAsyncDebounce(value => {
53            setGlobalFilter(value || undefined)
54        }, 700)
55
56        return (
57            <div className={Actions !== undefined ? 'flex flex-row justify-between' : 'flex flex-col'}>
58                {Actions !== undefined ? (<Actions />) : null}
59                <input
60                    value={value || ""}
61                    onChange={e => {
62                        setValue(e.target.value);
63                        onChange(e.target.value);
64                    }}
65                    placeholder={`${count} records...`}
66                    type="search"
67                    className={`input input-bordered input-sm w-full max-w-xs focus:outline-0 mb-2 ${Actions !== undefined ? '' : 'self-end'}`}
68                />
69            </div>
70        )
71    }
72
73    React.useEffect(() => {
74        let search = globalFilter === undefined ? '' : globalFilter
75        fetchData(pageSize, pageIndex, search);
76    }, [fetchData, pageIndex, pageSize, globalFilter, sortBy]);
77
78    return (
79        <>
80            <GlobalFilter
81                preGlobalFilteredRows={totalRow}
82                globalFilter={globalFilter}
83                setGlobalFilter={setGlobalFilter}
84            />
85            <div className="overflow-x-auto">
86                <table {...getTableProps()} className='table table-compact table-zebra w-full'>
87                    <thead>
88                        {headerGroups.map(headerGroup => (
89                            <tr {...headerGroup.getHeaderGroupProps()}>
90                                {headerGroup.headers.map(column => (
91                                    <th {...column.getHeaderProps(column.getSortByToggleProps())}>
92                                        <span>
93                                            {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' />}
94                                        </span>
95                                        {column.render('Header')}
96                                    </th>
97                                ))}
98                            </tr>
99                        ))}
100                    </thead>
101                    <tbody {...getTableBodyProps()}>
102                        {page.length > 0 ? page.map((row, i) => {
103                            prepareRow(row)
104                            return (
105                                <tr {...row.getRowProps()} className='hover'>
106                                    {row.cells.map(cell => {
107                                        return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
108                                    })}
109                                </tr>
110                            )
111                        }) : (<tr className='hover'>
112                            <td colSpan={10000} className='text-center'>Data not found!</td>
113                        </tr>)}
114                        {loading ? (
115                            <tr>
116                                <td colSpan="10000">Loading...</td>
117                            </tr>
118                        ) : null}
119                    </tbody>
120                </table>
121            </div>
122            <div className="flex flex-row justify-between">
123                <div className='mt-2'>
124                    <span>
125                        Halaman{' '}
126                        <strong>
127                            {pageIndex + 1} dari {pageOptions.length}
128                        </strong>{' '}
129                        Total <strong>{preGlobalFilteredRows.length}</strong>{' '}
130                    </span>
131                    <span>
132                        | Lompat ke halaman:{' '}
133                        <input
134                            type="number"
135                            defaultValue={pageIndex + 1}
136                            onChange={e => {
137                                const page = e.target.value ? Number(e.target.value) - 1 : 0
138                                gotoPage(page)
139                            }}
140                            className="input input-bordered input-sm w-20 max-w-xs focus:outline-0"
141                        />
142                    </span>{' '}
143                    <select
144                        value={pageSize}
145                        onChange={e => {
146                            setPageSize(Number(e.target.value))
147                        }}
148                        className="select select-bordered select-sm w-30 max-w-xs focus:outline-0"
149                    >
150                        {[10, 20, 30, 40, 50].map(pageSize => (
151                            <option key={pageSize} value={pageSize}>
152                                Tampilkan {pageSize} baris
153                            </option>
154                        ))}
155                    </select>
156                </div>
157                <div className='mt-2'>
158                    <button className='btn btn-xs' onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
159                        {'<<'}
160                    </button>{' '}
161                    <button className='btn btn-xs' onClick={() => previousPage()} disabled={!canPreviousPage}>
162                        {'<'}
163                    </button>{' '}
164                    <button className='btn btn-xs' onClick={() => nextPage()} disabled={!canNextPage}>
165                        {'>'}
166                    </button>{' '}
167                    <button className='btn btn-xs' onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
168                        {'>>'}
169                    </button>{' '}
170                </div>
171            </div>
172        </>
173    );
174}
175
176export 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.

1// file: pages/Example.js (parent)
2function Example() {
3
4    const [data, setData] = useState([]);
5    const [loading, setLoading] = useState(false)
6    const [pageCount, setPageCount] = useState(0)
7    const [totalRow, setTotalRow] = useState(0)
8    const [refresh, setRefresh] = useState(false)
9
10    const fetchData = useCallback(async (pageSize, pageIndex, search) => {
11        setLoading(true)
12        const queryOptions = {
13            page: pageIndex,
14            limit: pageSize,
15            search: search
16        }
17        const customers = await customerDatatable(queryOptions)
18
19        setData(customers.data)
20        setPageCount(customers.pagination.totalPage)
21        setTotalRow(customers.pagination.totalRow)
22        setLoading(false)
23        setRefresh(false)
24    }, [refresh]);
25
26    const columns = useMemo(
27        () => [
28            ....,
29            {
30                Header: 'Actions',
31                accessor: (row) => {
32                    return (
33                        <div className='flex flex-row w-38'>
34                            <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'>
35                                // here the child
36                                <FormEdit data={row} setRefresh={setRefresh} modalTarget={row.customer_uuid} />
37                            </ReuseableModal>
38                        </div>
39                    )
40                },
41                disableSortBy: true
42            }
43        ],
44        []
45    );
46
47    return (
48        <Fragment>
49            <Helmet>
50                <title>Example</title>
51            </Helmet>
52            <section className='p-3'>
53                <div className="bg-base-300 p-3 rounded">
54                    <TablePagination
55                        columns={columns}
56                        data={data}
57                        fetchData={fetchData}
58                        loading={loading}
59                        pageCount={pageCount}
60                        totalRow={totalRow}
61                    />
62                </div>
63            </section>
64        </Fragment>
65    )
66}
67
68export default PelangganAktif
69}

And here the modal popup

1// file: components/modal/FormEdit.js (child)
2function FormEdit({ data, setRefresh, modalTarget }) {
3
4    const { addToast } = useToast()
5    const initValues = data
6
7    const formSubmit = async (values) => {
8        const updated = await customerUpdate(values)
9        if (updated.type === 'success') {
10            addToast('success', 'top-right', 'Data updated!', `${data.profiles.fullname} detail updated`, 5000)
11            document.getElementById(`modalEdit-${modalTarget}`).click()
12            setRefresh(true)
13            resetForm()
14        } else {
15            addToast('error', 'top-right', 'Data failed to update!', `${data.profiles.fullname} defail failed to update`, 5000)
16        }
17    }
18
19    const { values, errors, touched, handleChange, handleSubmit, resetForm } = useFormik({
20        initialValues: initValues,
21        onSubmit: formSubmit,
22        enableReinitialize: true
23    })
24    
25    return // your form here
26}
27
28export default FormEdit

That's it!

No response

By supporting my work, you are not just sponsoring an individual; you are becoming a part of a collaborative movement. Together, we can foster an inclusive community, where creativity and innovation thrive, and where we inspire and empower each other to push the boundaries of what is possible.