A collection of React components which provide various functionality to child components in JSX/TSX. By using Render Props pattern, those components can be adopted declaratively just by surrounding children.
Works well when you create component which have some local states (e.g. radio group, toggle show/hide) or needs to show contents from APIs without propagating to global state (e.g. suggest, preview).
npm install reenhance-components
For CDN, you can use unpkg: https://unpkg.com/reenhance-components/umd/index.min.js
The global namespace is ReenhanceComponents
:
const { StateProvider, AsyncResolver, DebouncePropagator, ObjectHolder } = ReenhanceComponents;
Each components must be instantiated with initial parameters before using them in JSX/TSX.
Resolves async function and passes its result to children as props.
Property | Type | Required | Description |
---|---|---|---|
distinctKey | string | N | Name of prop to detect changes. Subject function is evaluated everytime value of the prop is changed. The default is 'subject'. |
initialProps | object | N | Initial props for children |
Property | Type | Required | Description |
---|---|---|---|
subject | Function | Y | An async function which returns promise to resolve |
(other props) | any | N | Arguments of subject |
Property | Type | Description |
---|---|---|
props | object | Resolved object from result of subject |
const asyncFetch =
({ query }) =>
fetch(queryToUrl(query))
.then(res => res.json())
.catch(err => ({ error: err.toString() }));
const AlbumsAsyncResolver = AsyncResolver('query', { resultCount: 0, results: [] });
const Albums = ({ query }) => (
<AlbumsAsyncResolver subject={asyncFetch} query={query} >
{(props) => (
...
)}
</AlbumsAsyncResolver>
);
Provides local state and updater to children as its props.
Property | Type | Required | Description |
---|---|---|---|
initialState | any | Y | Initial state |
nothing
Property | Type | Description |
---|---|---|
state | object | Current state object |
setState | Function | An updater for state. Takes new state as its argument |
const ToggleState = StateProvider(false);
const Toggle = () => (
<ToggleState>
{({ state, setState }) => (
...
)}
</ToggleState>
);
Debounces props propagation for given milliseconds.
See Debounce of ReactiveX docs for more details.
Property | Type | Required | Description |
---|---|---|---|
initialProps | object | Y | Initial props for children |
Property | Type | Required | Description |
---|---|---|---|
time | number | Y | Debounce time in milliseconds |
(other props) | any | N | Properties to propagete to children |
Property | Type | Description |
---|---|---|
props | object | Resolved object from result of subject |
const SuggestDebounce = DebouncePropagator({ status: 'loading' });
const Suggest = ({ query }) => (
<SuggestDebounce time={'200'} query={query}>
{({ query, status }) => ( // Propagation of 'query' is debounced in 200ms
...
)}
</SuggestDebounce>
);
Watches a property of an object and passes the latest value as an argument to children.
Property | Type | Required | Description |
---|---|---|---|
targetObject | object | Y | Immutable object to watch its property change |
Property | Type | Required | Description |
---|---|---|---|
watch | string | string[] | Y |
onChange | function | N | Called when the value of watching props is changed. Call signature is (newValue: any, oldValue: any, propName: string) => void |
Property | Type | Description |
---|---|---|
(any) | object | Proxied targetObject |
const RefWatcher = ObjectWatcher(React.createRef());
const DivRef = () => (
<RefWatcher watch="current">
{divRef => (
<div ref={divRef}>Hello ref.{divRef.current ? divRef.current.toString() : null}</div>
)}
</RefWatcher>
);
Composes multiple Components which have Render Props as their child.
Property | Type | Required | Description |
---|---|---|---|
children[i] | Component | Y | A component which have Render Props as their child. |
children[n - 1] | Component | Y | A render function which receives all props from preceding components |
const BooleanState = StateProvider<boolean>(true);
const NumericState = StateProvider<number>(3);
const MultiStateDiv = () => (
<Compose>
<BooleanState />
<NumericState />
{(b: StateAndUpdater<boolean>, n: StateAndUpdater<number>) => (
<div>
{b.state.toString()}:{n.state}
</div>
)}
</Compose>
);
- Q: Is this an alternative to Redux?
A: No. This module doesn't provide global state or flow pattern. - Q: Should I use this instead of Redux?
A: It depends. If the state or API response is local and per-instance, probably this module fits well. - Q: Is this better than HoCs?
A: Not sure. I think they are almost same. 😉 - Q: Can I rename arguments, 'state' and 'setState' of StateProvider?
A: Rename them in destructuring like({ state: isOpen, setState: setIsOpen })
. - Q: Why
StateProvider
passes the state as a property of object? It's confusing.
A: One reason is for renaming. Another reason isstate
andsetState
are inseparatable inStateProvider
. TryObjectWatcher
if you really don't want to usesetState
. - Q: Why don't you make
AjaxResolver
? It will be convenient. A: Requesting Ajax causes dependency to other APIs likefetch
,fetchJsonp
,Axios
, etc. This module is intended to solve only common part of problems.
This project aims these characteristics.
- declarative
- separation of view and logic
- can coexist with other modules
- works well with TypeScript