either(testFn: props => boolean, option: props => value ) (target: props => value) => value
Either takes a test function which creates an HOC to take another function and will execute that function if the function passes. It also has an option to take a "left-hand" argument to execute instead if the test function fails. If no option was provided, the HOC will return a null
.
- Examples:
const GreenOrRedLight = either(props => props.batteryCharged === '100%', RedComponent)(GreenComponent)
return <ChargingDisplay>
<GreenOrRedLight batteryCharged="88%" />
<ChargingDisplay/>
// renders the RedComponent
const MaybeDisplayHelpChat = either(prop => props.loggedIn === false)(HelpChat)
return <MaybeDisplayHelpChat loggedIn /> // renders nothing
either
is mainly inspired by recompose's branch
function, with a goal to be more concise and streamlined, defaulting to a "maybe" render by returning null
.
Unlike branch
- the either
HOC does not apply a "right hand" argument to target
- implying that it will only return the value of the target rather than renderComponent(target)
.
A value comes back from the last execution, rather than one more HOC wrapper. To emphasize this difference in application, either
has its "left-hand" and "right-hand" arguments reversed in comparison to branch
. In either
the "left" argument is the option for a failed test, and target
is what is returned upon success.
const SnitchOrKeepQuiet = either({ stayLoyal } => stayLoyal === true)(console.log)
const knowledge = SnitchOrStayQuiet({ stayLoyal: true, secrets: "Mob secrets" })
// null
A great advantage to using this "maybe-render" design pattern is to take the inline-conditionals out of your React components, and place the focus on Component composition.
Before:
const ComponentThatFetches = props => (
<div>
{ !props.isDataFetched ? <LoadingSpinner /> }
{props.isDataFetched && props.responseIsCorrupted && <ErrorMessage />}
{props.isDataFetched && !props.responseIsCorrupted && <DataVisual />}
{ props.isChatForumFetched ? <ChatForum /> : <LoadingSpinner />}
</div>)
// This is a giant jumble of conditional expressions
After:
const DataDisplay = either(props => !props.responseIsCorrupted, ErrorMessage)(DataVisual)
// Notice how this decorator function is decoupled from any target Component
const maybeLoadingScreen = either(props => props.isDataFetched, LoadingSpinner)
// Now we can apply a possible loading feedback to any component
const DataOrLoading = maybeLoadingScreen(DataDisplay)
const ChatDiscussion = maybeLoadingScreen(ChatForum)
// Our final top-level component becomes much more concise and clear in its declaration
const ComponentThatFetches = props => (
<div>
<RenderDataOrLoading {...props} />
<ChatForum responseFetched={props.isChatForumFetched} />
</div>
)