React Table Trigger Changed Without SWR

darkterminal avatar
GitHub Account@darkterminal
LanguageJAVASCRIPT
Published At2023-01-26 16:04:15

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 = ({
50    preGlobalFilteredRows,
51    globalFilter,
52    setGlobalFilter,
53  }) => {
54    const count = preGlobalFilteredRows;
55    const [value, setValue] = React.useState(globalFilter);
56    const onChange = useAsyncDebounce((value) => {
57      setGlobalFilter(value || undefined);
58    }, 700);
59
60    return (
61      <div
62        className={
63          Actions !== undefined
64            ? 'flex flex-row justify-between'
65            : 'flex flex-col'
66        }
67      >
68        {Actions !== undefined ? <Actions /> : null}
69        <input
70          value={value || ''}
71          onChange={(e) => {
72            setValue(e.target.value);
73            onChange(e.target.value);
74          }}
75          placeholder={`${count} records...`}
76          type="search"
77          className={`input input-bordered input-sm w-full max-w-xs focus:outline-0 mb-2 ${
78            Actions !== undefined ? '' : 'self-end'
79          }`}
80        />
81      </div>
82    );
83  };
84
85  React.useEffect(() => {
86    let search = globalFilter === undefined ? '' : globalFilter;
87    fetchData(pageSize, pageIndex, search);
88  }, [fetchData, pageIndex, pageSize, globalFilter, sortBy]);
89
90  return (
91    <>
92      <GlobalFilter
93        preGlobalFilteredRows={totalRow}
94        globalFilter={globalFilter}
95        setGlobalFilter={setGlobalFilter}
96      />
97      <div className="overflow-x-auto">
98        <table
99          {...getTableProps()}
100          className="table table-compact table-zebra w-full"
101        >
102          <thead>
103            {headerGroups.map((headerGroup) => (
104              <tr {...headerGroup.getHeaderGroupProps()}>
105                {headerGroup.headers.map((column) => (
106                  <th {...column.getHeaderProps(column.getSortByToggleProps())}>
107                    <span>
108                      {column.isSorted ? (
109                        column.isSortedDesc ? (
110                          <ArrowLongDownIcon className="h-4 w-4 inline mr-1" />
111                        ) : (
112                          <ArrowLongUpIcon className="h-4 w-4 inline mr-1" />
113                        )
114                      ) : (
115                        <FunnelIcon className="h-4 w-4 inline mr-1" />
116                      )}
117                    </span>
118                    {column.render('Header')}
119                  </th>
120                ))}
121              </tr>
122            ))}
123          </thead>
124          <tbody {...getTableBodyProps()}>
125            {page.length > 0 ? (
126              page.map((row, i) => {
127                prepareRow(row);
128                return (
129                  <tr {...row.getRowProps()} className="hover">
130                    {row.cells.map((cell) => {
131                      return (
132                        <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
133                      );
134                    })}
135                  </tr>
136                );
137              })
138            ) : (
139              <tr className="hover">
140                <td colSpan={10000} className="text-center">
141                  Data not found!
142                </td>
143              </tr>
144            )}
145            {loading ? (
146              <tr>
147                <td colSpan="10000">Loading...</td>
148              </tr>
149            ) : null}
150          </tbody>
151        </table>
152      </div>
153      <div className="flex flex-row justify-between">
154        <div className="mt-2">
155          <span>
156            Halaman{' '}
157            <strong>
158              {pageIndex + 1} dari {pageOptions.length}
159            </strong>{' '}
160            Total <strong>{preGlobalFilteredRows.length}</strong>{' '}
161          </span>
162          <span>
163            | Lompat ke halaman:{' '}
164            <input
165              type="number"
166              defaultValue={pageIndex + 1}
167              onChange={(e) => {
168                const page = e.target.value ? Number(e.target.value) - 1 : 0;
169                gotoPage(page);
170              }}
171              className="input input-bordered input-sm w-20 max-w-xs focus:outline-0"
172            />
173          </span> <select
174            value={pageSize}
175            onChange={(e) => {
176              setPageSize(Number(e.target.value));
177            }}
178            className="select select-bordered select-sm w-30 max-w-xs focus:outline-0"
179          >
180            {[10, 20, 30, 40, 50].map((pageSize) => (
181              <option key={pageSize} value={pageSize}>
182                Tampilkan {pageSize} baris
183              </option>
184            ))}
185          </select>
186        </div>
187        <div className="mt-2">
188          <button
189            className="btn btn-xs"
190            onClick={() => gotoPage(0)}
191            disabled={!canPreviousPage}
192          >
193            {'<<'}
194          </button>{' '}
195          <button
196            className="btn btn-xs"
197            onClick={() => previousPage()}
198            disabled={!canPreviousPage}
199          >
200            {'<'}
201          </button>{' '}
202          <button
203            className="btn btn-xs"
204            onClick={() => nextPage()}
205            disabled={!canNextPage}
206          >
207            {'>'}
208          </button>{' '}
209          <button
210            className="btn btn-xs"
211            onClick={() => gotoPage(pageCount - 1)}
212            disabled={!canNextPage}
213          >
214            {'>>'}
215          </button>{' '}
216        </div>
217      </div>
218    </>
219  );
220}
221
222export 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  const { addToast } = useToast();
4  const initValues = data;
5
6  const formSubmit = async (values) => {
7    const updated = await customerUpdate(values);
8    if (updated.type === 'success') {
9      addToast(
10        'success',
11        'top-right',
12        'Data updated!',
13        `${data.profiles.fullname} detail updated`,
14        5000
15      );
16      document.getElementById(`modalEdit-${modalTarget}`).click();
17      setRefresh(true);
18      resetForm();
19    } else {
20      addToast(
21        'error',
22        'top-right',
23        'Data failed to update!',
24        `${data.profiles.fullname} defail failed to update`,
25        5000
26      );
27    }
28  };
29
30  const { values, errors, touched, handleChange, handleSubmit, resetForm } =
31    useFormik({
32      initialValues: initValues,
33      onSubmit: formSubmit,
34      enableReinitialize: true,
35    });
36
37  return; // your form here
38}
39
40export default FormEdit;

That's it!

A demo/repos link

No response

Share This Story