A HOC for separating hooks & logic from the presentational component in React.
npm install compose-hooksMake your React Components purely presentational, and simply bind the props for them in a declarative manner.
#### Why?
When I tried some different "compose hooks" libraries on npm, for each one I missed some feature or configurability. None of them supported the full control I felt required for all situations, so this version enables just a few key control features.
- 0 dependencies, but of course React is a peer dependecy.
composeHooksThis HOC (Higher Order Component) wraps a component in order to attach hooks, props & behaviors.
It enables the separation of presentation from logic & behavior, like this:
``js
// MyComp.jsx
const MyCompPresenter = ({ ...props }) => (
{Here is the pure JSX content,}which means these type of pure component functions Never have bodies, only direct returns of JSX.
{}This is because all logic, hooks etc is bound via composeHooks and enter here as props.
{}A good pattern is for these presenter components to always end in "...Presenter".
{}
);
export const MyComp = composeHooks({
// Hooks and prop creations (See below)
})(MyCompPresenter);
`
First and foremost:
- Each _value_ will be executed as a function at render.
Then, the composeHooks basically have 4 behaviors for the key-values in the object given to it:
- _Props_ from the parent will by default override the props from composeHooks.>
- A prefix in the _key string_ will inject all props up until that point into the _value function_.!
- A postfix in the _key string_ will make this prop override that prop possibly coming from the parent.use
- The result of a value function call will be spread into _props_ if the _key string_ begins with followed by a capital letter, as in e.g. useValue. (Otherwise the result will directly be assigned to the prop.)
Let's see some examples of what the key-values can be and look like.
`jstheme
export const MyComp = composeHooks({
/ Simplest use. The hook returns an object that will be spread into props. /
useExpanded,
/ Hook returns something/anything that we want to assign to prop. /!
theme: useTheme,
/ We can use a simple function to return a constant value, and... /
noUppercase: () => true,
/ ...with the syntax overwrite the possible incoming prop with the same name. /labelStyle
'icon!': () => 'arrow-right',
/ Same as above with an object put into (and overriding parent prop). /theme
'labelStyle!': () => ({ flex: 1 }),
/ Putting a ">" first will inject all previous props into the function. Hence, we can use here. />
'>color': ({ theme }) => theme.colors.onPrimary,
/* Combine & ! to use incomming props and then overwrite.style
(This pattern is especially useful for .) */useCustomHook
'>style!': ({ theme, style }) => ({
backgroundColor: theme.colors.onSurfaceVariant,
...style,
}),
/* If our custom hook needs some props, but we want the returned object to`
be spread into all final props, we can give it a dummy name that follows the naming convention
of React Hooks. */
'>useSpread': useCustomHook,
/ Using e.g. ramda can be a nice point-free style way of selecting/using something from a hook call. /
t: R.pipe(useTranslation, R.head),
})(MyCompPresenter);
A possible output (props for the Presenter) from the above composed hooks etc could be:
`js``
const MyCompPresenter = ({
expanded,
toggleExpanded,
theme,
noUppercase,
icon,
labelStyle,
color,
style,
someValueFromCustomHook,
anotherValueFromCustomHook,
t,
}) => (
// ...
);
Happy coding!
MIT