react-parm
Handle react classes with more functional purity
Table of contents
Summary
react-parm
is a thin abstraction providing partial-application methods that allow you to handle react
classes with much more functional purity. This allows for better encapsulation, greater separation of concerns, and simplified testing. When combined with destructuring, it also improves readability and comprehension.
Usage
;; const componentDidMount = propsshouldGetFoo && ; const onClickGetBar = ; Component // lifecycle methods componentDidMount = ; // refs element = null; // instance methods onClickGetBar = ; { return <button data-baz="quz" onClick=thisonClickGetBar ref= > Go get bar! </button> ; }
Methods
createMethod
Create a functional instance or lifecycle method, which will receive the full instance as the first parameter.
createMethod(instance: ReactComponent, method: function, ...extraArgs: Array): (instance: ReactComponent, args: Array, extraArgs: Array) => any
;; const componentDidMount = ; const onClickDoThing = props; state = isMounted: false ; componentDidMount = ; onClickDoThing = ; { return <div> <h3>Welcome to doing the thing</h3> <button onClick=thisonClickDoThing>Do the thing</button> </div> ; }
If you want this method to be memoized in an instance-specific way, you can assign the function that will memoize the method to the memoizer
property on the function you create the method from.
; const setCount = ; setCountmemoizer = memoize;
This will automatically wrap the method you pass to createMethod
in the memoizer
.
createValue
Create a value to assign to the instance based on a functional method which will receive the full instance as the first parameter.
createValue(instance: ReactComponent, method: function, ...extraArgs: Array): any
;; const getLength = { return propsfoolength;}; length = ; { return <div>The length of the foo parameter is thislength</div>; }
createRender
Create a functional render method, which will receive the props
as the first parameter, the full instance as the second parameter, and any arguments passed to it as the third parameter.
createRender(instance: ReactComponent, render: function): (props: Object, instance: ReactComponent, args: Array) => ReactElement
;; const componentDidMount = ; const DoTheThing = { return <div> <h3>Welcome to doing the mounted thing</h3> <span>Am I mounted? isMounted ? "YES!" : "No :("</span> <button onClick=doThing>Do the thing</button> </div> );}; state = isMounted: false ; componentDidMount = ; render = ;
NOTE: The difference in signature from createMethod
is both for common-use purposes, but also because it allows linting tools to appropriately lint for PropTypes
.
createRenderProps
Create a functional render props method, which will receive the props
passed to it as the first parameter, the full instance as the second parameter, and any additional arguments passed to it as the third parameter.
createRenderProps(instance: ReactComponent, render: function): (props: Object, instance: ReactComponent, remainingArgs: Array) => ReactElement
;; const RenderPropComponent = <div></div>; const renderProps = <div> propsstuff <button onClick=instancepropsdoThing>Do the thing</button> </div>; const DoTheThing = <RenderPropComponent>renderProps</RenderPropComponent>; state = isMounted: false ; renderProps = ; render = ;
NOTE: The main difference between createRender
and createRenderProps
is the first props
argument. In the case of createRender
, it is the props
of the instance
the method is bound to, whereas in the case of createRenderProps
it is the props
argument passed to it directly.
createComponent
Create a functional component with all available instance-based methods, values, and refs a Component
class has.
createComponent(render: function, options: Object): ReactComponent
;; const state = isMounted: false; const componentDidMount = ; const onClickDoThing = props; const DoTheThing = <div> <h3>Welcome to doing the thing</h3> <button onClick=doThing && onClickDoThing>Do the thing</button> </div>; DoTheThingdisplayName = "DoTheThing"; DoTheThingpropTypes = doThing: PropTypesfuncisRequired; DoTheThing componentDidMount onClickDoThing state;
NOTE: Starting in version 2.6.0
, the options
can be applied via currying:
componentDidMount onClickDoThing state DoTheThing;
The component will be parmed with createRender
, and the properties passed in options
will be handled as follows:
-
Lifecycle methods will be parmed with
createMethod
-
Instance methods will be parmed with
createMethod
, unless:-
It has a static property of
isRender
set totrue
, in which case it will be parmed withcreateRender
. Example:const renderer = <div>foo</div>;rendererisRender = true; -
It has a static property of
isRenderProps
set totrue
, in which case it will be parmed withcreateRenderProps
. Example:const renderProps = <div>
-
-
Instance values will be assigned to the instance
There are also some additional properties that are treated outside the context of assignment to the instance:
getInitialState
=> if a method is passed, then it is parmed and used to derive the initial state instead of the staticstate
propertygetInitialValues
=> If a method is passed, then it is parmed and used to derive initial instance values- Expects an object to be returned, where a return of
{foo: 'bar'}
will result ininstance.foo
being"bar"
- Expects an object to be returned, where a return of
isPure
=> shouldPureComponent
be used to construct the underlying component class instead ofComponent
(defaults tofalse
)onConstruct
=> If a method is passed, then it is called with the instance as parameter at the end of construction
NOTE: Any additional static values / methods you apply to the render component will be re-assigned to the parmed component.
createComponentRef
Create a method that will assign the Component requested to an instance value using a ref callback.
createComponentRef(instance: ReactComponent, ref: string): (component: HTMLElement | ReactComponent) => void
;; component = null; { return <SomeOtherComponent ref=> We captured the component instance! </SomeOtherComponent> ; }
The ref
string value passed will be the key that will be used in the assignment to the instance
.
createElementRef
Create a method that will assign the DOM node of the component requested to an instance value using a ref callback.
createElementRef(instance: ReactComponent, ref: string): (component: HTMLElement | ReactComponent) => void
;; element = null; { return <SomeOtherComponent ref=> We found the DOM node! </SomeOtherComponent> ; }
The ref
string value passed will be the key that will be used in the assignment to the instance
.
createCombinedRef
Create a method that will assign both the DOM node of the component requested and the component itself to a namespaced instance value using a ref callback.
createCombinedRef(instance: ReactComponent, ref: string): (component: HTMLElement | ReactComponent) => void
;; someOtherComponent = null; { return <SomeOtherComponent ref=> I have the best of both worlds! thissomeOtherComponent will look like "{component: SomeOtherComponent, element: div}" </SomeOtherComponent> ; }
The value assigned will be an object with component
and element
properties, which reflect the component and the DOM node for that component respectively. The ref
string value passed will be the key that will be used in the assignment to the instance
.
createPropType
Create a custom PropTypes validation method.
createPropType(validator: function): (metadata: Object) => (Error|null)
; const isFoo = ;
The full shape of the metadata
object passed to createPropType
:
component: string // the name of the component key: string // the key that is being validated name: string // the name of the prop being validated path: string // the full path (if nested) of the key being validated props: any // the props object value: any // the value of the prop passed
Please note that usage may result in different values for these keys, based on whether the custom prop type is used in arrayOf
/ objectOf
or not.
When used in arrayOf
or objectOf
:
key
represents the nested key being validatedname
represents the name of the prop that was passedpath
represents the full path being validated
Example:
const isArrayOfFoo = ;...<SomeComponent bar='baz'>// The key "0" for prop "bar" at path "bar[0]" is "baz" in "SomeComponent", when it should be "foo"!
When the prop type is used in any context other than arrayOf
/ objectOf
, then key
, name
, and path
will all be the same value.
Why parm?
PARM is an acronym, standing for Partial-Application React Method. Also, why not parm? It's delicious.
Development
Standard stuff, clone the repo and npm install
dependencies. The npm scripts available:
build
=> run rollup to build development and productiondist
filesdev
=> run webpack dev server to run example app / playgroundlint
=> run ESLint against all files in thesrc
folderlint: fix
=> runslint
with--fix
prepublish
=> runsprepublish:compile
when publishingprepublish:compile
=> runlint
,test:coverage
,transpile:lib
,transpile:es
, andbuild
test
=> run AVA test functions withNODE_ENV=test
test:coverage
=> runtest
but withnyc
for coverage checkertest:watch
=> runtest
, but with persistent watchertranspile:lib
=> run babel against all files insrc
to create files inlib
transpile:es
=> run babel against all files insrc
to create files ines
, preserving ES2015 modules (forpkg.module
)