Creating reusable UI components on React is no small feat. React provides the proper tools and abstractions to accomplish said reusability of using the same unit of code across applications and deployment mediums, like web, mobile, tv, etc.
Simple visual UI components with no state are reusable on the boundaries of medium (web, mobile, etc.) as long as the styling part is consistent with the rest of the application. So, if you have a simple Button component, depending on how is styled then you can make it reusable across applications. Most React component libraries also ship the CSS/Less/Sass they used and this forces users of this libraries to adopt the same styling across their apps. We can see examples of this with react-bootstrap (https://react-bootstrap.github.io/) or react-md (https://react-md.mlaursen.com/) both using external CSS files you have to add to your build process.
Once you cross the medium boundary and try to reuse the same components on mobile, with react-native for example, different styling technologies make this task close to impossible in most of cases.
Styling apart, When we add a little bit of state management, things starts to get even more complex for reusability.
Many times I seen, and created myself, components with state management built in. There is nothing wrong with it, it just doesn’t help much to make our components reusable. Any component logic, no matter how small, being managed internally within the component has the potential to derail us from the reusable path by mixing two concerns: logic and presentation.
I will try to illustrate my point with an example. Lets make an editable cell component.
The EditableCell component
An EditableCell in this example is a simple component. It has two modes, display and edit modes. In display mode, it will show a value, once clicked it will turn into the edit mode allowing the user to modify the value via an input box. Hitting enter on the input or losing focus (by clicking in another area of the screen) will end the edit mode, submitting the value to whatever callback the component has assigned. We are not interested in styling.
The state management this component has is simple enough to keep it close to the component. So, no fancy flux stores/dispatchers here. That would be an overkill, and also, will make us suffer from the same problem styling components has. Once you decided for a flux library you are basically forcing/limiting your end users.
Lets make a first implementation of this component.
This component as you can see, have a little bit of state, including: editing and changedValue. 3 internal functions manage this state: changeValue, startEditing and checkEndChanging.
Simple enough? yes, reusable? not much.
Meaning the visual structure and logic of this component are intrinsically united. We cannot separate them, so, we can’t reuse same logic with a different appearance or even different medium (ex. mobile).
A more reusable EditableCell
Lets try to represent our EditableCell as a separation of logic and visual structure.
First we have logic. For this I will first code the logic in a redux-like fashion. Similar as described at https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367
This logic module, exports 3 things: initialState, a reducer, a createActions function. This bit of logic is completely reusable across platforms, even without React involved. Is universal, testable, predictable.
Then… we can make a HOC component that takes a visual representation of an EditableCell in the form of a component and wraps it with the state management we designed in the logic file. (For this I used a helper function I created https://www.npmjs.com/package/react-setstate-connect)
At this point our logic is no longer, universal, but, constrained to React components. Although good enough to be used on react web or react-native mobile since no visual component is yet defined.
And last part, our Web visual component.
This one, is basically a pure rendered component, with no state management whatsoever. State is added by wrapping the component with the withEditableCellLogic function.
Lets see it in action…
It some practical way, the later demo’s code looks bigger, it is. The first demo being so much tightly integrated logic/view allows for simpler code constructs. But, that is a small compromise if we need to deal with bigger abstractions on state management. Imagine for a second you want to provide both a web and mobile versions of your app. Abstracting all state management allows you to share more code between codebases, and just redefining how web or mobile components will look, using the proper native components each platform provides (divs, View, etc.).
Abstracting state management is easy achieved with a more comprehensive flux library. I am talking here about state that lies on components that don’t need to be managed globally.
From the “You might not need Redux” post I loved this quote:
Provide alternative UIs while reusing most of the business logic.
This is exactly what we want to achieve. Extracting state from React components is one of the easiest ways to make it reusable.