Skip to content

Instantly share code, notes, and snippets.

@marlun78
Last active November 7, 2018 08:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marlun78/210835372e197d2ee0a9768b109ed46f to your computer and use it in GitHub Desktop.
Save marlun78/210835372e197d2ee0a9768b109ed46f to your computer and use it in GitHub Desktop.
Idea for a React <Switch> component
/**
* react-switch-component.js
* Copyright (c) 2018 marlun78
* MIT License, https://gist.github.com/marlun78/bd0800cf5e8053ba9f83
*/
import { Children, createElement } from 'react';
/**
* Switch Component
*
* @example
* <Switch expression="2">
* <div case="1">Not rendered</div>
* <div case="2">This is rendered</div>
* <div default>Not rendered</div>
* </Switch>
*
* <Switch expression="5">
* <div case="1">Not rendered</div>
* <div case="2">Not rendered</div>
* <div default>This is rendered</div>
* </Switch>
*
* <Switch expression="2">
* <div case={1}>Not rendered</div>
* <div case={2}>Not rendered</div>
* <div default>This is rendered because "2" !== 2</div>
* </Switch>
*
* <Switch expression={2} strict={false}>
* <div case={1}>Not rendered</div>
* <div>This child be always be rendered...</div>
* <div case={2}>This is rendered</div>
* <div default>Not rendered</div>
* </Switch>
*/
export const Switch = (props) => {
const { children, expression, strict = true } = props;
if (!props.hasOwnProperty('expression')) {
throw new TypeError('Switch component requires an `expression` prop');
}
const childrenArray = Children.toArray(children);
return strict === true
? strictSwitch(childrenArray, expression)
: looseSwitch(childrenArray, expression);
};
// Strict mode (default) will only render one child and will exclude children
// without `case` nor `default` prop.
const strictSwitch = (children, expression) => {
for (let i = 0, l = children.length; i < l; i++) {
const child = children[i];
const { props, type } = child;
const hasDefaultProp = props.hasOwnProperty('default');
const hasCaseProp = props.hasOwnProperty('case');
const {
children: childChildren,
case: childCase,
default: childDefault,
...childProps
} = props;
if (hasDefaultProp === false && hasCaseProp === false) {
throw new Error(
'In strict mode, all Switch components children requires a `case` or `default` prop',
);
}
if (hasDefaultProp === true || childCase === expression) {
return createElement(type, childProps, childChildren);
}
}
return null;
};
// Loose mode will include children without `case` nor `default` prop and
// matched case or default child.
const looseSwitch = (children, expression) => {
let found = false;
return children.reduce((accumulator, child) => {
const { props, type } = child;
const hasCaseProp = props.hasOwnProperty('case');
const hasDefaultProp = props.hasOwnProperty('default');
const {
children: childChildren,
case: childCase,
default: childDefault,
...childProps
} = props;
// Add all children without `case` or `default` prop
if (hasCaseProp === false && hasDefaultProp === false) {
return [...accumulator, child];
}
if (found === false) {
if (hasDefaultProp || (hasCaseProp && expression === childCase)) {
found = true;
return [
...accumulator,
createElement(type, childProps, childChildren),
];
}
}
return accumulator;
}, []);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment