Well, the way I see it (and the canonical way React sees it), a render function f producing a view V from some data D is defined as
V = f(D)
… which is an oversimplification, since UI commonly has local state, which definitely doesn’t need to be elevated all the way to the master data. Any interactive UI thus certainly uses several sources of truth.
Let’s add some to the previous. Let our previous data D now be D_master since it is our master business data (which we may not change in any way, shape, or form). Then, add state of app routing D_router, user auth state D_auth, and local widget state D_widget.
V = f(D_master, D_router, D_auth, D_widget)
We might have all kinds of access checks regarding the three first ones, but here we are only interested in what this means for rendering a view.
To use all our data, let’s imagine a form where a dropdown select widget is placed inside a tabbed container. The user needs to have a particular user role in their auth data to see this widget. There are many similar widgets in this form, all with fine-grained controls.
- The options for the list come from D_master.
- The state of the tabbed container lives in the URL so that it isn’t lost on refresh, so it is accessed through D_router.
- The user role comes from D_auth.
- And finally, whether the dropdown is open or not is stored in the component’s local state, D_widget.
This is not a far-fetched example, such things are common in more complicated applications.
In any case, it does make intuitive sense to me to allow logic in the render function, as long as that logic operates directly on the render function parameters and not on some on-the-fly transformed intermediate.
Rules of refactoring also apply.
Without logic in the render function, the coupling between frontend and backend would get insane, as every component fetching data would require custom on-the-fly denormalisation for that data in the backend. In our example, we would be pre-generating data for the entire form for a particular user, probably even for unused features.
It would be like generating snippets of HTML in response to AJAX requests, but without the prerendering to HTML.
Let’s add some to the previous. Let our previous data D now be D_master since it is our master business data (which we may not change in any way, shape, or form). Then, add state of app routing D_router, user auth state D_auth, and local widget state D_widget.
We might have all kinds of access checks regarding the three first ones, but here we are only interested in what this means for rendering a view.To use all our data, let’s imagine a form where a dropdown select widget is placed inside a tabbed container. The user needs to have a particular user role in their auth data to see this widget. There are many similar widgets in this form, all with fine-grained controls.
- The options for the list come from D_master.
- The state of the tabbed container lives in the URL so that it isn’t lost on refresh, so it is accessed through D_router.
- The user role comes from D_auth.
- And finally, whether the dropdown is open or not is stored in the component’s local state, D_widget.
This is not a far-fetched example, such things are common in more complicated applications.
In any case, it does make intuitive sense to me to allow logic in the render function, as long as that logic operates directly on the render function parameters and not on some on-the-fly transformed intermediate.
Rules of refactoring also apply.
Without logic in the render function, the coupling between frontend and backend would get insane, as every component fetching data would require custom on-the-fly denormalisation for that data in the backend. In our example, we would be pre-generating data for the entire form for a particular user, probably even for unused features.
It would be like generating snippets of HTML in response to AJAX requests, but without the prerendering to HTML.