Provide a component, state management, and effect for handling storage of lists of "flagged" items.
Simply wrap the section of the component tree that needs to access state information about the currently flagged items:
<FlaggableProvider namespace="ns1" defaultState={{
flagged: {
ns1: MyGetItemsArrayOfIds(ns1)
}
}}>
<AnItem>
<h2>An Item!</h2>
<Flag id={id} namespace="ns1" />
</AnItem>
</FlaggableProvider>
Users may pass any (sync) function they like to 'set' the list of flagged items per namespace. This function
should accept parameters of namespace
and id
. This function is invoked by useEffect
in the Flag
component.
Implementors are responsible for hydrating the list of flagged items per namespace. Something like:
const e = document.getElementById('thing');
const namespace = 'ns1';
const currentItems = getItems(namespace);
ReactDOM.render(
<FlaggableProvider
namespace={namespace}
defaultState={{
flagged: {
[namespace]: currentItems,
},
}}
>
<FlagCounter namespace={namespace} label="Favorites" />
<Flag id="things" namespace={namespace}>Things</Flag>
<Flag id="things2" namespace={namespace}>Things2</Flag>
<Flag id="things3" namespace={namespace}>Things3</Flag>
</FlaggableProvider>, e,
);
It is completely safe to maintain separate trees of flagged items.
Simply set the namespace
prop on the FlaggableProvider
A common UI workflow might be to have a flag counter in the top of an app. And a list of items available for flagging in the content section. If the application can't render both elements in the same tree (usually when the entire application is not delivered by React). Using a Portal can allow implementors to render the flag elements anywhere they choose:
/**
* Wrap flags in a portal so they can be rendered outside of the
* component tree.
* @type {NodeListOf<Element>}
*/
const flagDomNodes = document.querySelectorAll('.favorites-flag'); // <-- These guys are down the page.
const Flags = () => [...flagDomNodes].map(f => ReactDOM.createPortal(<Flag
className="fa fa-heart"
namespace="favorites"
id={f.getAttribute('data-id')}
/>, f));
const favoritesCounter = document.querySelector('.favorites-counter'); // <-- This guy is in the header.
const favs = getItems('favorites');
if (favoritesCounter) {
ReactDOM.render(
<FlaggableProvider
namespace="favorites"
defaultState={{
flagged: {
'favorites': favs,
},
}}
>
<FlagCounter namespace="favorites" label="Favorites" />
{ flagDomNodes.length ? <Flags /> : null }
</FlaggableProvider>, favoritesCounter,
);
}
There is a very simple test application in demo/index.html
. It loads index.jsx
. You can run the
webpack dev server with:
$ npm run start
Test with:
$ npm run test