Custom filter button in module disappears after page navigation

Hi everyone!

I’ve created a custom component (a filter button) inside a module in UI Bakery. When I first load the application, the button renders correctly on the page.

However, after navigating to other pages in the application and then returning, the button is no longer visible.

Has anyone faced a similar issue? Is there something specific I need to do to make sure the custom component in the module stays rendered after page navigation?

Any suggestions or workarounds would be greatly appreciated!

Thanks in advance!

Hi @nasoviva,

I’ve had this happen, but I can’t really remember what exactly was going on, but it probably was problematic code.

A few questions to narrow the problem down:

  • What component did you use to make this filter button?
  • In the action run by the On Init trigger inside the module, what are you doing there?
  • Do you see any errors in the devtools (F12)?

Thanks for your help! Here’s some more context:

  • I’m using a Custom Component .
  • I have an onData action where I execute some JavaScript ({{data}}), and at the end, I run a setSortValue action with this code: {{ui.customComponent.value}}.
  • Would you mind having a look at my custom component code? I have a feeling I might’ve messed something up there.
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8" />
    <title>Sort Component</title>
    <!-- 3rd party scripts and styles -->
    <script src="https://unpkg.com/react@17/umd/react.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js" crossorigin></script>
    <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

</head>

<body>
    <div class="root"></div>

    <script type="text/babel">
        function SortComponent() {
            const sortData = UB.useData();
            console.log('sortData in app:', sortData);

            // Load existing sorts if provided from UB.useData
            React.useEffect(() => {
                if (sortData && sortData.sort) {
                    loadSorts(sortData);
                }
            }, [sortData]);

            // Close dropdown when clicking outside the entire sort component
            React.useEffect(() => {
                function handleClickOutside(event) {
                    const dropdownElement = document.querySelector(".sc-dropdown");
                    if (dropdownElement && !dropdownElement.contains(event.target)) {
                        closeDropdown();
                    }
                }
                document.addEventListener("mousedown", handleClickOutside);
                return () => {
                    document.removeEventListener("mousedown", handleClickOutside);
                };
            }, []);

            const toggleDropdown = () => {
                document.getElementById("sc-dropdown-content").classList.toggle("show");
            };

            const closeDropdown = () => {
                document.getElementById("sc-dropdown-content").classList.remove("show");
            };

            const fields = [
                { name: "Contact Status", value: "p.contact_status" },
                { name: "Campaign Title", value: "c.title" },
                { name: "Created At", value: "p.created_at" },
            ];

            function addSortRow(sortItem = null) {
                const sortContainer = document.getElementById("sc-rows-container");
                const sortDiv = document.createElement("div");
                sortDiv.className = "sc-row";

                const fieldSelect = document.createElement("select");
                fields.forEach((field) => {
                    const option = document.createElement("option");
                    option.value = field.value;
                    option.innerText = field.name;
                    if (sortItem && sortItem.field === field.value) {
                        option.selected = true;
                    }
                    fieldSelect.appendChild(option);
                });

                const directionSelect = document.createElement("select");
                ["ASC", "DESC"].forEach((direction) => {
                    const option = document.createElement("option");
                    option.value = direction;
                    option.innerText = direction;
                    if (sortItem && sortItem.direction === direction) {
                        option.selected = true;
                    }
                    directionSelect.appendChild(option);
                });

                sortDiv.appendChild(fieldSelect);
                sortDiv.appendChild(directionSelect);

                const removeButton = document.createElement("button");
                removeButton.className = "sc-remove-sort-button";
                removeButton.innerText = "X";
                removeButton.onclick = () => sortDiv.remove();
                sortDiv.appendChild(removeButton);

                sortContainer.appendChild(sortDiv);
            }

            function generateSort() {
                const sortContainer = document.getElementById("sc-rows-container");
                const sortRows = sortContainer.getElementsByClassName("sc-row");
                const sortCriteria = Array.from(sortRows).map((row) => {
                    const field = row.querySelector("select:nth-of-type(1)").value;
                    const direction = row.querySelector("select:nth-of-type(2)").value;
                    return {
                        field: field,
                        direction: direction,
                    };
                });

                const result = sortCriteria;
                document.getElementById("sc-result").innerText = JSON.stringify(
                    result,
                    null,
                    2
                );
                UB.updateValue(result);
                UB.triggerEvent(result);
                // Close the dropdown after applying the sort
                closeDropdown();
            }

            function loadSorts(data) {
                document.getElementById("sc-rows-container").innerHTML = ""; // Clear existing sorts
                data.sort.forEach((sort) => addSortRow(sort));
            }

            function resetSorts() {
                document.getElementById("sc-rows-container").innerHTML = "";
            }

            return (
                <div className="sc-dropdown">
                    <button className="sc-dropdown-button" onClick={toggleDropdown} style={{
                        backgroundColor: sortData.color || '#FEB400',
                        border: `1px solid ${sortData.color || '#FEB400'}`
                    }}
                    >
                        +Sort
                    </button>
                    <div id="sc-dropdown-content" className="sc-dropdown-content">
                        {/* Close button */}
                        <button className="sc-close-button" onClick={closeDropdown}>
                            <svg
                                width="16"
                                height="16"
                                viewBox="0 0 24 24"
                                fill="currentColor"
                            ></svg>
                        </button>
                        <div className="sc-container">
                            <div className="sc-header">
                                <div>
                                    <button
                                        className="sc-add-sort-button"
                                        onClick={() => addSortRow()}
                                    >
                                        Add Sort Criterion
                                    </button>
                                </div>
                                <div>
                                    <button
                                        className="sc-internal-button"
                                        onClick={() => {
                                            resetSorts();
                                            generateSort();
                                            closeDropdown();
                                        }}>
                                        Reset
                                    </button>
                                    &nbsp;
                                    <button
                                        className="sc-generate-sort-button"
                                        onClick={generateSort}
                                    >
                                        Generate Sort
                                    </button>
                                </div>
                            </div>
                            <div id="sc-rows-container"></div>
                            <pre id="sc-result" style={{ display: "none" }}></pre>
                        </div>
                    </div>
                </div>
            );
        }

        const SortComponentWrapper = UB.connectReactComponent(SortComponent);
        ReactDOM.render(
            <SortComponentWrapper />,
            UB.container.querySelector(".root")
        );

        UB.onDestroy(() =>
            ReactDOM.unmountComponentAtNode(UB.container.querySelector(".root"))
        );

    </script>
</body>

</html>

Now I understand; you used the Custom Component/Unrestricted Component inside a module.

For me, it didn’t work whatsoever with Custom Component, only with the Unrestricted Component.

Furthermore, looking through the code, it looks fine; I couldn’t spot any immediate issues with it.

I quickly build something similar to what you describe, using the code you provided. An Unrestricted Component inside a Module and the only trigger I added was on the Unrestricted Components On Event trigger to push the value with {{module.triggerEvent(value)}} outside the module.

With the module set up, I placed it in an app and changed the pages, but nothing seems different?

nasoviva

If you could share a video or something of the incorrect behavior maybe I’ll notice something.

Thank you so much for taking the time to look into it and even recreate the setup — I really appreciate it!

I’ve recorded a short video to show what I’m seeing on my side. The sorting itself works as expected, but as soon as I change the page, the component disappears completely — and that’s the issue I’m currently stuck with.

If you have a chance to take a look at the video, I’d be very grateful for any thoughts or suggestions you might have.

Thanks again!

Thanks for sharing a video. Sadly, I couldn’t see anything of interest.
I’ve tried to copy the CSS, thinking that could have an issue, but also no.

But since the button doesn’t show when loading the page, this points very likely to some runtime error. When a page loads, all of its contents are loaded fresh. Then, when you change pages, all components are unmounted and the components of a different page are mounted. So I assume there must be some error during the init phase of the component.

I recommend you to:

  • check the devtools console for any errors
  • look through the functions, actions, etc. that automatically run on startup when the page is loading
  • sporadically place console.log() in the functions, actions, etc. that run on startup to see if it stops anywhere or if a variable has an invalid/unwanted value

And since I still was unable to replicate the issue with most of the code of the custom component, the problem is very likely in one of the trigger actions. Or a different component/code interferes in some kind of way.

Thanks a lot, Max!

I really appreciate your detailed explanation and suggestions :folded_hands:

I’ll follow your advice and check the triggers and startup actions again — that might be exactly where the issue is.

Thanks again for taking the time to help!

2 Likes

No problem :slight_smile: don’t hesitate to ask further if you still can’t find it

1 Like