epic-react
v1.0.0
Published
Not epic but handy helpers for conditional React rendering.
Downloads
5
Readme
epic-react
Not epic but handy helpers for conditional React rendering. Functional utilities to quickly implement recurring rendering patters in a readable way.
Jump directly to the epic.
Usage
npm install react epic-react
import React from 'react'
import { when } from 'epic-react'
export const DaytimeTheme = (time: number) =>
when(
time > 6 && time < 18,
() => <Daylight />,
() => <Nighttime />
)
Available Methods
import { not, when, epic, until, list, random } from 'epic-react'
not
If the provided condition is true nothing will be rendered.
export const CartButton = (stock: number) =>
not(stock === 0, <Button onClick={Store.addToCart}>Buy</Button>)
when
If the condition is true render the component and if it's false render the fallback if one is provided.
export const DaytimeTheme = (time: number) =>
when(
time > 6 && time < 18,
() => <Daylight />,
() => <Nighttime /> // Optional
)
epic
Usually there is more than an if else required for rendering. In this case an epic will help:
// Usage as an object: specifying conditions along with components.
epic
.loading(() => <p>Loading...</p>, false)
.error(() => <p>Error...</p>, false)
.fallback(() => <p>Fallback...</p>, false)
.done(() => <p>Epic done</p>)
// Usage as a function: specifying conditions first.
epic({
loading: false,
error: false,
fallback: false,
})
.loading(() => <p>Loading...</p>)
.error(() => <p>Error...</p>)
.fallback(() => <p>Fallback...</p>)
.done(() => <p>Epic done</p>)
The second option is especially handy if you already have an object with the conditions available or can create a matching state.
until
Asynchronous rendering depending on the state of a Promise.
until<string, null>(
new Promise<string>((done) => setTimeout(() => done('resolved!'), 3000)),
(result) => <p>{result}</p>,
<p>loading...</p>
)
If the Promise is rejected an optional error handler will be rendered.
until<string, string>(
new Promise<string>((done, fail) =>
setTimeout(() => fail('rejected...'), 3000)
),
result => (
<p>{result}</p>
),
<p>loading...</p>,
error => (
<p>{error}</p>
)
)}
list
const ListElement = ({ value }: { value: number }) => <span>{value}</span>
This epic makes rendering lists quicker.
list<{ value: number }>([{ value: 1 }, { value: 2 }, { value: 3 }], ListElement)
As the third parameter you can pass an element which will be rendered in case list is empty.
list<{ value: number }>([], ListElement, <span>It's an empty list ;)</span>)
An optional separator element can be inserted in between elements, similar to the join() function for regular Arrays.
list<{ value: number }>(
[{ value: 1 }, { value: 2 }, { value: 3 }],
ListElement,
<span>List is empty...</span>
<span>,</span>
);
random
Randomly picks a component from the list of arguments.
random(
() => <p>first</p>,
() => <p>second</p>
)
Comparison with other Abstractions
Vanilla JS
Simply writing all the logic yourself works just fine. These epics however have been created due to very similar parts occurring over and over again.
// Vanilla JS
export const AsyncFetchedData = (data) => {
if (data.loading) {
return <Loading />
}
if (data.error) {
return <Error />
}
return <Data data={data.result} />
}
// with an epic
export const AsyncFetchedData = (data) => epic
.loading(() => <Loading />, data.loading)
.error(() => <Error>, data.error)
.done(() => <Data data={data.result} />);
Higher Order Components
import { Suspense } from 'react'
const LazyComponent = React.lazy(() => import('./ProfilePage'))
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
)
import { until } from 'epic-react'
return until(import('./lazy'), (result) => <result.default />, <p>Loading...</p>)
Suspense (HOC): 4 Lines of Code
until (react-epic): 1 Line of Code 🤓
Event Handlers
Shortcuts to do something when a certain key is pressed. To be used with onKeyDown
, onKeyPress
or onKeyUp
.
import { onEnter, onEscape } from 'epic-react'
onEnter
<input onKeyUp={onEnter((event) => submit())} />
onEscape
<input onKeyDown={onEscape((event) => close())} />
Several Keys
<input
onKeyPress={(event) => {
onEnter(() => submit())(event)
onEscape((event) => close(event))(event)
}}
/>