on-update
v2.0.0
Published
on-update
Downloads
23
Readme
onUpdate
$ npm i on-update --save
The onUpdate is a pattern for state flow between React components.
It defines a contract for data to bubble up the render tree, and update the root state object, without the need of extra libraries and imports. It puts the logic for updating the domain data in response to UI interaction into the React components, in parallel with the logic that does the opposite process, maps the domain data into UI elements (the render function).
This makes the data flow explicit and localized into self sustained components, it also allows for reusable components that are not dependent of other data management libraries, like flux, redux, etc.
React components can be understood as functions that map the data from the domain into interactive UI elements, each component receives data as props
and returns UI elements (or other components that will ultimately return UI elements). The render function of a component contains the main logic for executing this mapping.
Interactions on the UI elements leads to change in the domain data, the modified domain data when rendered generates a new UI which is the result of the user interaction. The problem in React applications is how to architecture the code to solve the update of the application domain data in response to UI interaction in a way that is resilient, understandable and scalable.
Libraries like flux and redux define specific methods for transforming a centralized state object in response to UI interaction. We advocate here that's possible to achieve the same objectives as those libraries with pure React components.
onUpdate Data Flow
Functional React components map the data they receive to UI elements. Components are broken into sub components for managing complexity, readability and reusability; each component should be focused into solving the relation between the properties they receive and the components and elements they return.
With the onUpdate pattern, the data for the application is hold by the root component, only transient state should be hold by other components of the application, when the data changes on the top level component it flows down and the components are re-rendered accordingly.
Interactions on leaf components should trigger callbacks that bubbles up to the root component to change its domain data. This flow is solved here on a component level, each component needs to handle both the mapping from his props to the children it renders as also the update of his own props according to the update of the props of his children.
When an interaction callback occurs (like onClick, onMouseEnter, etc.) the component calls his props.onUpdate
method with an object containing an updated version of his own props
, according to the result of the interaction.
A parent component may receive an onUpdate call from any of its children with their updated props
, the parent component will then create a new object with the update version of his props
according to the props
sent by his child onUpdate call. He will then call his own props.onUpdate
passing up his updated props
.
This pattern continue up the component tree, as each component remaps the updates of its child props
back to an update of its own props
. When the bubble up reaches the root component, and updates the centralized state object, it then triggers a re-render of the components with the updated data.
With this pattern each component contains the full logic of a specific part of the problem that he's solving in a application. The problem of mapping its received data into children components, and handling the update of his props
according to the update of its children props
.
When you add inline style and a theme property to the mix, you get a component that's fully self-sustained and reusable across projects and applications, without the need of any extra library or bootstrap.
Also, any part of the data flow of the application can be understood by looking at two files: the parent and the children component. By following this pattern, the tree structure formed by the components of a well developed application can work as a map that explain how the domain data unfolds into the UI elements, and also the opposite, how the UI elements go into updating the domain data.
Basic Example
import React, {PropTypes} from 'react'
import _ from 'lodash'
import {Block} from 'react-display'
export const handleAnnotationUpdate = (props, annotationProps) => {
props.onUpdate({
...props,
text: annotationProps.text,
})
}
const ExampleComponent = props => (
<Block>
<Annotation
text={props.text}
onUpdate={_.partial(handleAnnotationUpdate, props)}
/>
</Block>
)
ExampleComponent.propTypes = {
onUpdate: PropTypes.func,
text: PropTypes.string.isRequired,
}
export default ExampleComponent
Helper
During development it's possible to use the stateHOC
wrapper helper.
import {stateHOC} from 'on-update'
// component definition
export default stateHOC(ExampleComponent)
The wrapper extends the props' passed to the component with an
onUpdatemethod, when it's called the props are stored as
stateon the wrapper and the component is re-rendered with the new
props`.
This helps during the development of new components, as the flow up of new data can be done incrementally from bottom up.