React Component Ordering in Dom

Viewed 278

I have a react provider containing a number of components that I'm trying to get the order of in the DOM. I'm not concerned with the exact value that orders them but I need to know if one element is before or after another for other logic I need to perform in the provider. My app would look like this:

// App.jsx

return (
    <Provider>
        <Element /> // 1
        {showSecond && <Element />} // 2
        <div>
            <Element /> // 7
        </div>
        <Element> // 14
            <Element /> // 21
        </Element>
    </Provider>
);
// Element.jsx

const orderValue = getMagicOrderValue() // <-- Looking for this
const provider = useContext(providerContext);

useEffect(() => {
    provider.add(orderValue);
    return () => { provider.remove(orderValue); };
}, [orderValue, provider]);

return children;

Some of these components maybe nested or come and go per unrelated logic. I plan to report the ordering value to the provider context inside a useEffect and I'm not concerned should it change so long as the components then order correctly. Is there a build-in function or value that would help order these elements as they would appear in the dom?

1 Answers

It appears useLayoutEffect can help solve this issue. These are called blocking and in order of the "dom". Using a variable outside the hooks to count the layoutEffect calls and update the order state. However, the cpu could go nuts with all the repeated state updates so orderingTime is used to ensure the state can't update more than once per layoutEffect in a single render.

let orderCount = 0;
let orderingTime = 0;

function useOrderingRoot() {
    useLayoutEffect(() => {
        orderCount = 0;
        orderingTime = Date.now();
    });
}

function useOrdering() {
    const [[time, index], setIndex] = useState(() => [
        orderingTime,
        orderCount + 1
    ]);

    useLayoutEffect(() => {
        const order = orderCount++;

        // Prevent cpu thrashing
        if (time === orderingTime) return;

        if (order === index) return;
        setIndex([orderingTime, order]);
    });

    return index;
}

I ended up pushing this whole solution to an upstream module and publishing it to npm make-list-provider

Related