Searching the inner text content of nested react nodes

Articles|HÃ¥kon Underbakke | over 3 years ago

I came across an interesting challenge recently while I was building a flexible searchable list component. I wanted it to work so that you could pass any JSX or react components into the list, and be able to search it just like a normal string. I came up with a neat recursive function to help me out, let’s go through it step by step.

Building the function

The goal of this recursive function is to end up returning a string. Javascript will read the react node as either an array of objects (and possibly strings), a single object, or a string. Let’s first check if the node is an array, and then return each of the plain strings, while running the function again on each of the objects (nodes).

Case: Multiple children

In the case of multiple children, reactNode will be an array.

const getRecursiveChildText = reactNode => {
    if (Array.isArray(reactNode)) {
        // Multiple children
        let joinedNodes = [];
        reactNode.forEach(node => {
            if (typeof node === "object") joinedNodes.push(getRecursiveChildText(node));
            else if (typeof node === "string") joinedNodes.push(node);
        });
        return joinedNodes.join(" ");
    }
};

Now that we’ve dealt with the case of it being an array, let’s deal with the cases of it being a single child.

Case: Single children

In the case of a single child, it will either be an object (new node) or a string.

const getRecursiveChildText = reactNode => {
    const children = reactNode.props.children;
    if (Array.isArray(reactNode)) {
        // Multiple children
        let joinedNodes = [];
        reactNode.forEach(node => {
            if (typeof node === "object") joinedNodes.push(getRecursiveChildText(node));
            else if (typeof node === "string") joinedNodes.push(node);
        });
        return joinedNodes.join(" ");
    }
    if (typeof children === "object") {
        // Found direct child
        return getRecursiveChildText(children);
    }
    if (typeof children === "string") {
        // Found searchable string
        return reactNode.props.children;
    }
};

Finished function

To finish the function off, let’s just add a little failsafe above the single child checks. I have also added a very simple search function, just to demonstrate how getRecursiveChildText might be used.

const getRecursiveChildText = reactNode => {
    const children = reactNode.props.children || undefined;
    if (Array.isArray(reactNode)) {
        // Multiple children
        let joinedNodes = [];
        reactNode.forEach(node => {
            if (typeof node === "object") joinedNodes.push(getRecursiveChildText(node));
            else if (typeof node === "string") joinedNodes.push(node);
        });
        return joinedNodes.join(" ");
    }
    if (children === undefined) {
        if (typeof reactNode === "string") return reactNode;
        else return " ";
    }
    if (typeof children === "object") {
        // Found direct child
        return getRecursiveChildText(reactNode.props.children);
    }
    if (typeof children === "string") {
        // Found searchable string
        return reactNode.props.children;
    }
};

const searchNode = (node, que) => {
    const searchableText = getRecursiveChildText(node);
    if (searchableText.toUpperCase().search(que.toUpperCase()) >= 0){
       /// found match...
       return true;
    }
    return false;
}