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.
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 /> )import { not, when, epic, until, list, random } from 'epic-react'If the provided condition is true nothing will be rendered.
export const CartButton = (stock: number) => not(stock === 0, <Button onClick={Store.addToCart}>Buy</Button>)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 )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.
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> ) )}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> );Randomly picks a component from the list of arguments.
random( () => <p>first</p>, () => <p>second</p> )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} />);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 🤓
Shortcuts to do something when a certain key is pressed. To be used with onKeyDown, onKeyPress or onKeyUp.
import { onEnter, onEscape } from 'epic-react'<input onKeyUp={onEnter((event) => submit())} /><input onKeyDown={onEscape((event) => close())} /><input onKeyPress={(event) => { onEnter(() => submit())(event) onEscape((event) => close(event))(event) }} />