Custom DT button to reload/refresh DataTable using React Component

Custom DT button to reload/refresh DataTable using React Component

pain19pain19 Posts: 62Questions: 6Answers: 0

I have searched the forums/web and found some solutions, but none are quite what I am looking so I thought I'd ask to see if others have already solved this issue.

SUMMARY: I have a working product in an app store, but I am being forced to rewrite it as the vendor is sun-setting the current framework (javascript based) in favor of their new one (react javascript based). So far, I have ported everything successfully and it is working perfectly. I'm at the point where I need to refresh the table using a custom Datatable button, but can't figure it all the way out.

SETUP: I have a main file where I make the necessary API calls then I pass the data to my Datatable component in another file. This is all working perfectly with no issues/errors.

PROBLEM: Where I am stuck is I have a row of Datatable buttons and the last button is a custom "refresh" button. In the old app, I simply called the functions that built the table initially with a parameter to know that it was a refresh. Using the DT React Component setup, I'm not sure how to reload the table using the custom Datatable button.

WORKAROUND: I can easily add a button outside of the Datatable and trigger the table/data state using useState. And I have a working version. But it looks weird to have a row of buttons and then this one-off button elsewhere in the app.

DESIRED SOLUTION: I would like to keep the existing custom Datatable button so it remains with the other Datatable buttons. I have tried triggering the useState, other functions, etc. but the way React seems to be designed, what I am trying to do/access is hidden. For example, I can export a function and access it, but that function cannot see the useState info because that is buried under a Component. If I access things directly, I get the Hook Error #321 as I am violating the hook rules.

I hope my description makes sense and I have attached a screenshot hoping that helps a bit more.

FYI: I tried to post my code but got a message saying this post would be too long. Let me know if you still want to see it and I'll post it in blocks instead.

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 64,210Questions: 1Answers: 10,597 Site admin
    Answer ✓

    You need to get an instance of the DataTables API. The documentation for the React component for DataTables details how that can be done here. Then you just need to call ajax.reload() (assuming it is an Ajax sourced table).

    Allan

  • pain19pain19 Posts: 62Questions: 6Answers: 0
    edited March 25

    @allan I tried that, but I don't have an ajax source so I am working on changing/updating the state--which the link also discusses under Reactive Data. I also found something that discusses Parent/Child components, so I am also exploring that option. I know there's a solution and it is most likely very simple, but being new to React I have to learn the ins & outs of it--which I will of course. B)

    As always, thx for the quick response.

  • pain19pain19 Posts: 62Questions: 6Answers: 0

    @allan I got it working, but I have one more question before I settle on a final solution.

    I construct the buttons in a separate file and pass it into the DT component. You can see all of the buttons in my previous screenshot. But when passed the refresh button in, I get "reloadData is not defined". But if I put the exact same code directly into the table, it works just fine.

    CODE SNIPPET (passing this in)

        // Used to refresh the table without refreshing the browser window.
        var butRefresh = [
            {
                // Setup the button code and action.
                "text": '<i class="fa-solid fa-arrows-rotate"></i>',
                "titleAttr": 'Refresh the table',
                "className": 'rw-button',
                "action": function ( e, dt, node, config ) {
                    reloadData();
                }
            }
        ];
    

    DT CODE

    top2Start: {
         buttons: buttons
    },
    

    CODE SNIPPET where I add the button code directly to DT

    top2Start: {
         buttons: [
              {
                   // Setup the button code and action.
                   "text": '<i class="fa-solid fa-arrows-rotate"></i>',
                    "titleAttr": 'Refresh the table',
                    "className": 'rw-button',
                     "action": function ( e, dt, node, config ) {
                            reloadData();
                       }
               }
          ]
    },
    

    Is there a reason why it recognizes the reloadData function when directly coded vs it being passed in? I'll keep trying things until I hear back from you, but the important thing is I have a working refresh button using the DT buttons instead of that odd HTML button. B)

  • kthorngrenkthorngren Posts: 21,837Questions: 26Answers: 5,048
    edited March 26 Answer ✓

    My guess is the scope where you are creating var butRefresh doesn't have access to the scope where the function is located. For example:
    https://live.datatables.net/bacavosa/1/edit

    Since it can't find the function object it's undefined.

    If you move the function outside of document.ready(), at the same scope where butRefresh is created, then it works. For example:
    https://live.datatables.net/xikuxidu/1/edit

    Kevin

  • pain19pain19 Posts: 62Questions: 6Answers: 0

    @kthorngren ok, based on your examples (and assistance from @allan ), I was able to figure it all out.

    In short, I did the following.

    The initial issue was resolved by creating the following in the main file and tracking the reload values via useState()

    const handleReloadData = () => {
       fetchData();
    }
    

    Then I added it to my call to the DT component like so:

    <DataTable className="display cell-border" tableId={prjActiveTableName} data={activeProjects} columns={prjActiveColumns} sort={prjActiveOrderBy} tabName="Active Projects" buttons={prjActiveButtons} detailRowData={prjActiveDetailRowDef} baseUrl={baseUrl} popupArea={prjPopupArea} reloadData={handleReloadData}></DataTable>
    

    Added the reloadData={handleReloadData} piece which was missing and caused my initial scope issue.

    My DT component accepted all of these parameters like so:

    const DataTable = ({ tableId, data, columns, sort, tabName, buttons, detailRowData, baseUrl, popupArea, reloadData }) => {
       // All DT code is in here.
    }
    

    In my file where I constructed the button set, I removed the setup of the refresh button and did the following in the DT component file.:

                    top2Start: {
                        buttons: [ buttons,
                            {
                                // Setup the button code and action.
                                "text": '<i class="fa-solid fa-arrows-rotate"></i>',
                                "titleAttr": 'Refresh the table',
                                "className": 'rw-button',
                                "action": function ( e, dt, node, config ) {
                                    reloadData();
                                }
                            }
                        ]
                    },
    

    Here I still pass in the constructed button set, but the Refresh button is "always" the last button so it was ok to implement the code in this manner. Doing this resolved my second scope issue.

    Hopefully this makes sense and will help others. But the main issues were not passing the reload function into the DT component (scope issue #1) and then constructing the refresh button in another file and passing it in (scope issue #2).

    As always, THANKS for the assistance!!

  • pain19pain19 Posts: 62Questions: 6Answers: 0

    UPDATE: While the previous solution worked, it bothered the hell out of me to not have "buttons: buttons", so I kept playing with it and now I pass in the entire button set and use initComplete to add the "on click" action. I could be totally wrong, but this just feels cleaner to me. ;)

                initComplete: function() {
                    const api = this.api();
                    const myButton = api.buttons("Refresh:name");
                    
                    if (myButton.any()) {
                        myButton.nodes().each(function() {
                            $(this).on("click", function() {
                                console.log("Adding custom action here");
                                reloadData();
                            })
                        })
                    }
    
  • allanallan Posts: 64,210Questions: 1Answers: 10,597 Site admin

    Awesome! Thanks for posting back with your updates, and delighted to hear that you got it working as you need.

    Allan

Sign In or Register to comment.