drx v0.11.1
Dr. X
Declarative React Experiments
Install
npm install --save drx
Let's see the example right away pls
Q. Is this ready to use in production?
Nope! Things are most likely going to change.
Q. What is this? Is it even a good idea?
I'm still figuring that out :D But I'm discovering a lot of useful patterns arising from this idea of writing components (structure & logic) declaratively.
In short, the goal is to provide a way of creating components by describing the dependencies between their props.
Take a look at the examples below if you're keen, and let me know what you think.
Getting started
Let's start with the simplest possible example:
import x from 'drx'
export default x({
className: 'message',
children: ''
})
This is the rough equivalent of:
import React, { PureComponent } from 'react'
export default class extends PureComponent {
render () {
const { children, className } = this.props
return (
<div className={className || 'message'}>
{children}
</div>
)
}
}
So we saved a few lines by using drx
. That won't always be the case, sometimes we'll end up with more lines than a traditional approach. But I hope we can get to the point of gaining much more value from those extra lines.
Example: translating props
A common bit of display logic in components is translating props from a parent to a child. For example, this component receives an imageUrl
prop which becomes the src
of a child image element:
import React, { PureComponent } from 'react'
export default class extends PureComponent {
render () {
const { children, imageUrl } = this.props
return (
<div className='message'>
{ imageUrl && <img src={imageUrl} className='message__image' /> }
<h1 className='message__heading'>{props.heading || 'Default Heading'}</h1>
<span className='message__text'>{ children }</span>
</div>
)
}
}
There are 3 prop translations happening in the example above:
imageUrl
becomes thesrc
of the imageheading
becomes thechildren
of the h1 (with fallback to a default value)children
becomes thechildren
of the span
We've also got some display logic to say we don't want to render an <img>
element if we don't have an imageUrl
.
To write the above with drx
we'll get something like this:
import x from 'drx'
const Root = x({
className: 'message',
imageUrl: '',
children: '',
heading: 'Default Heading'
})
const Image = x.img({
className: 'message__image',
src: Root.imageUrl
})
const Heading = x.h1({
className: 'message__heading',
children: Root.heading
})
const Text = x.span({
className: 'message__text',
children: Root.children
})
Root.children(
Heading,
props => props.imageUrl && Image,
Text
)
export default Root
Reading from the top:
a component
Root
, with some default propsa component
Image
- renders an
<img>
with classnamemessage__image
- maps the
Root.imageUrl
prop to thesrc
attribute of the<img>
- renders an
a component
Heading
- renders an
<h1>
with classnamemessage__heading
- maps the
Root.heading
prop to the heading'schildren
. If noheading
prop is provided toRoot
, we'll get the default heading value fromRoot
's definition.
- renders an
a component
Text
- renders a
<span>
with classnamemessage__text
- adopts the
children
of theRoot
component as its ownchildren
- renders a
finally we tell
Root
to render withHeading
,Image
andText
as its childrenImage
will only be rendered if we have a truthyimageUrl
prop
Example: reducing props
Sometimes a child's prop is a function of 1 or more parent props. We can declare this with x.from
. It both defines the dependency (ensuring that the props are passed down) and calls the function to transform or reduce the original values.
import x from 'drx'
const Root = x({
caption: '',
imageUrl: '',
secure: false
})
const Text = x.div({
children: x.from(Root.caption, p => p.caption.toUpperCase())
})
const Image = x.img({
alt: Root.caption,
src: x.from(
Root.imageUrl, Root.secure,
p => `${p.secure ? 'https' : 'http'}://example.com/${p.imageUrl}`
)
})
export default Root.children(
Image,
Text
)