Reputation: 6491
I have a simple React component that wraps its children
:
function Wrapper(props) {
return <div>{props.children}</div>;
}
If I pass an invalid child, this component will throw an error:
const plain_object = {};
// This throws an error
ReactDOM.render(<Wrapper>{plain_object}</Wrapper>, container);
What I'd like to have instead is to check if the children I'm passing in will render without error, for any valid "renderable" value. For example:
function SafeWrapper(props) {
// Note, this function doesn't exist, but I want it to
if (!isValidChildren(props.children)) {
return null;
}
return <div>{props.children}</div>;
}
const plain_object = {};
// This will no longer throw
ReactDOM.render(<SafeWrapper>{plain_object}</SafeWrapper>, container);
I know that React has React.isValidElement
, but this doesn't check for primatives, or any other items that would otherwise render fine:
// The following calls all return `false` despite them
// not throwing errors when you try and pass them in the `children` prop
// Note that not all of these will render (e.g. the falsy values are skipped)
// but they still don't throw.
React.isValidElement('');
React.isValidElement('Hello');
React.isValidElement(0);
React.isValidElement(1);
React.isValidElement(null);
React.isValidElement(undefined);
React.isValidElement([1]);
React.isValidElement([<div />]);
Is there a function isValidChildren
like I mentioned in my example? If not, what would writing one look like?
/**
* Returns `true` if `children` can be passed without throwing. `false` if otherwise.
*/
function isValidChildren(children) {
// ?
}
Upvotes: 0
Views: 2253
Reputation: 5786
It doesn't directly answer your question about implementing a function to check, but you could use an ErrorBoundary
with an inner empty wrapper to handle cases of unrenderable content:
class ErrorBoundary extends React.Component {
state = { error: null, errorInfo: null }
static getDerivedStateFromError(error, errorInfo) {
return { error, errorInfo }
}
render() {
if (this.state.error) {
return <span>something went wrong</span>
}
return this.props.children
}
}
// needed because the error boundary catches errors
// in *nested* components, whereas if it tried
// to render the children directly, it can't catch
// errors in its own `render`
const Inner = ({ children }) => children
const SafeWrapper = ({ children }) => (
<ErrorBoundary>
<Inner>{children}</Inner>
</ErrorBoundary>
)
That said, there is a bit of a code smell if you're trying to render things without knowing what they are. I can't envision a use case where you would want to just attempt to throw random values at the DOM.
Upvotes: 0
Reputation: 3885
You could do something like this:
const isRenderable = (node) => {
let renderable;
switch (typeof node) {
case "string":
case "number":
renderable = true;
break;
default:
if (Array.isArray(node) && node.length) {
renderable = node.reduce((acc, e) => acc && isRenderable(e), true);
break;
}
renderable = React.isValidElement(node);
break;
}
return renderable;
};
Upvotes: 1