How to get the Redux State in a React 18 production build via the browser's console

Written in August 21, 2022 - 🕒 2 min. read

A few months ago I wrote a blog post about how to get the Redux State in production, but with the recent release of React 18, that code doesn’t work anymore.

Let’s dive into why it doesn’t work anymore and figure out how to create a new script that works.

What changed?

On React 17, the React container contained a property called _reactRootContainer, and this is not the case for React 18 anymore, as we can see in the React source code, it now uses __reactContainer as a prefix with a random key in the end.

With this in mind, we can try to find the React container by checking if it contains a property starting with __reactContainer, but before that, we need to find all potential DOM elements that could be the React container.

Most React apps will use an HTML id to set the container and then query it with getElementById(), so to query all elements with an id in the document we can use document.querySelectorAll("*[id]").

To find all React containers on the page, we can run the following script:

const internalRoots = Array.from(document.querySelectorAll("*[id]")).map((el) => {
    const key = Object.keys(el).filter((keyName) => keyName.includes('__reactContainer')).pop();

    return el[key];
}).filter((key) => key);

Done! Now all we have to do is find the _internalRoot and we’re set, right? Right?!?!

No

To be honest, that first part was quite easy to figure it out, and since I’m not very familiar with the React source code, after trying out diving into all the properties available in the container element, I decided to go with the lazy solution.

Ok so we know that we can get the Redux State via the getState() function, so all we have to do is a tree search inside the React container variable. Easy.

Thankfully, there is already a GitHub Gist for this, so all we have to do is adapt it to our case, so we look into a specific variable instead of at window.

const stores = new Set();
// code from https://gist.github.com/mindplay-dk/1843c267fc633688059dfa5e3b07d0dd
internalRoots.forEach((root) => {
    let seen = new Map();
    function search(obj) {
        if (seen.has(obj)) {
            return;
        }

        seen.set(obj, true);
        for (name in obj) {
            if (name === 'getState') {
                stores.add(obj);
            }

            if ((obj?.hasOwnProperty?.(name)) && (typeof obj[name] === "object") && (obj[name] != null)) {
                search(obj[name]);
            }
        }
    }

    search(root);
});

Done, now we can access the states with [...stores].map((store) => store.getState()).

redux state on instagram

You can copy and paste the full script below.

const internalRoots = Array.from(document.querySelectorAll("*[id]")).map((el) => {
    const key = Object.keys(el).filter((keyName) => keyName.includes('__reactContainer')).pop();

    return el[key];
}).filter((key) => key);

const stores = new Set();
// code from https://gist.github.com/mindplay-dk/1843c267fc633688059dfa5e3b07d0dd
internalRoots.forEach((root) => {
    let seen = new Map();
    function search(obj) {
        if (seen.has(obj)) {
            return;
        }

        seen.set(obj, true);
        for (name in obj) {
            if (name === 'getState') {
                stores.add(obj);
            }

            if ((obj?.hasOwnProperty?.(name)) && (typeof obj[name] === "object") && (obj[name] != null)) {
                search(obj[name]);
            }
        }
    }

    search(root);
});

[...stores].map((store) => store.getState());

See you in the next one!

Tags:


Post a comment

Comments

No comments yet.