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;
}