Last active
November 7, 2018 08:27
-
-
Save marlun78/210835372e197d2ee0a9768b109ed46f to your computer and use it in GitHub Desktop.
Idea for a React <Switch> component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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