component()
This section describes how to create Reactive Components using component() API.
component() creates a react functional component which is bound to the reactive data. It is able to create components in two different ways accepting two different functional component definition. First overload accepts an ordinary react functional component and acts like a higher order component. Second overload accepts "reactive component definition" which is a function that returns a renderer function. Reactive component definition enables a persistent local scope that will be created once for each component and disposed when the component is unmounted, eliminating all the quirks of 'hooks'.
Defining a Reactive Component
This is the recommended way of defining components especially if component has local state or logic. Reactive component structure is very similar to a functional component. component() accepts a function that returns another function returning React node. This allows a closure to be created which persists as long as the component is alive.
Component Scope
The function given to component() will be called only one time creating a closure allowing us to define the local variables and state to be used by the actual renderer. Whatever is defined in that scope will stay and won't be re-created in each renders. So no need to use useMemo or useCallback hooks to prevent things to be re-created. In fact hooks API won't work in this scope
So this is the place we define the local component state, callbacks or any variables to be used by the renderer, as well as life-cycle hooks
Renderer
The renderer is the function that uses the reactive values or props to produce the markup (virtual node as JSX) for the component. This function is used as a computed value by the reactivity system which means any update of the reactive data which is accessed in this scope will cause a render. The render result is cached and won't be calculated again unless any dependent reactive value is changed.
Defining state
State is defined by using reactive or ref which accepts any object in any shape. When state is mutated any component using that state will get rendered.
Reactive values can be defined in Component scope as well as outside the component to be shared by multiple components easily.
Reacting to props
Props are also reactive so that they can be referenced in computed values (such as renderer function), they can be watched and can be used in effect
When does a reactive component render?
Under the hood the renderer function of a reactive component is called in an effect. As any other effect callback it is called only if some referenced reactive data is updated. In other words, unreferenced reactive data update won't cause any render.
As long as the mutation of state are sync, no matter how many state variables are changed there will be only one render after the mutations.
In the example above, the unusedValue may seem to be used in the renderer at first look and the component may be expected to re-render as a result of updating this variable but this is not correct. Actually it's not used in the render and it does not affect the rendered content. It's referenced in the callback and the callback function is not part of the rendered markup. Reactive components are smart to only render if a reactive value is referenced in the resulting markup.
Lifecycles and Handle / Ref
Since we leverage the whole reactivity system we no longer need react hooks. No useState is needed since state is managed with reactive values. No useMemo, useRef or useCallback because whatever we define in the component scope will persist. And also no dependency arrays needed since there is no stale state scenario. Here are the list of hooks can be called in the component local scope.
onMounted() / onUnmounted()
Both accepts a callback to be called when the component is mounted or unmounted. onMounted also accepts a function returning a cleanup function which will be called when the component is unmount. This can be useful to cleanup things in the same scope.
Or with cleanup function
This lifecycle dependent logic can also be extracted and reused as follows
imperativeHandle and component.withHandle()
component.withHandle is used to create component with a forwarded ref just like React.forwardRef. You can expose the ref as any object to parent components using imperativeHandle.
Context Api
React context API works the same way in reactive components. When a context is provided it can be consumed using consumeContext
from the component local scope.
Defining a Functional Component
This is the other overload of component() which accepts an ordinary 'react functional component' and acts like a HOC and returns a new component that is bound to the reactive data.
It's already ref forwarded so you don't need to use forwardRef
.
Interoperability with React components
Reactive plays well with ordinary React components, class or functional. At the end of the day component() produces a memoized functional components with hooks. You can still use existing react components in the tree, pass props of callbacks.
If you still feel like to use React hooks Api just make a wrapper component with hooks and use render props to pass necessary data to a reactive component.
Last updated
Was this helpful?