-
Notifications
You must be signed in to change notification settings - Fork 25
Rendering from the top down
Clearwater renders your entire app from the top down and only when you tell it to. This differs from how React renders on state changes — because React has no concept of an "app", you explicitly tell it to render a component tree to a DOM node, but you call this.setState
to update the component's state and re-render.
In Clearwater, however, there is an "app" concept: the Clearwater::Application
. Application state shouldn't be in many of your components; only your root component and routing targets should maintain any state at all and pass them into the ephemeral components as brand-new objects.
One of the things people primarily bring up when discussing top-down rendering is that it's inefficient. It's entirely possible that Clearwater could track the DOM node where each component gets rendered, but that has its own performance costs. Instead, Clearwater apps can use component render caching to get around this.
Re-rendering absolutely everything every time the app renders is the surest way to have the freshest view of your app state, but is frequently unnecessary. The virtual DOM has to generate new virtual nodes, then compare them to the previous version, and make the necessary changes to the rendered DOM. If your component can be sure it has no changes between then and now, it can reuse the previous rendered virtual nodes and save some CPU time.
class TodoList
include Clearwater::Component
include Clearwater::CachedRender
attr_reader :todos
def initialize(todos)
@todos = todos
end
def should_render?(previous)
todos != previous.todos
end
def render
ul(todos.map { |todo| TodoListItem.new(todo) })
end
end
Notice the should_render?
method. We check the todos
array against the one in the previously rendered TodoList
component. We return true
if anything has changed, signaling that the component should go ahead and rerender. Returning a falsy value tells the component that it can safely reuse the previously rendered value. Returning the exact same virtual DOM tree (the exact same object in memory, not just an equivalent tree) between renders triggers an optimization in the diffing algorithm that causes it not to descend into the tree, which can save precious time in rerendering your app.
If you depend on mutable data at all in your components, you will want to dup
it. For example, if you append a new todo to the list rather than creating a new array with the new todo added to the end, you should set @todos = todos.dup
in TodoList#initialize
. Similarly, you'll want to dup
the individual tasks in the TodoListItem
component so that when you compare it you won't be comparing the model with itself.