Mount
Class-based components provide a powerful and convenient way for you to create modular chunks of UI. However, sometimes they are overkill. If you just need to output some static markup, a component is not necessary. Instead you can define a function that returns the markup. This is sometimes called a functional component
. You can read the docs for functional components to learn more about how to create and use them. Once you've created a function component, you can inject it into the document with this mount
function. To use it, you will need to import it into your code:
import { h, mount } from 'composi'
mount
takes two parameters: tag and container. Tag is either a JSX tag or an h
function. container is the DOM node or a valid CSS selector to query that node. If no container is provided or the DOM query finds no match, it will append the tag to the document body.
The mount
function always returns an element which you can use as a reference to the DOM tree that needs to be updated by the render
function. We show how to do this below when we go into details about the render
function.
Here is an example of how to mount a functional component:
import { h, mount } from 'composi'
// Define functional component:
function Title({message}) {
return(
<nav>
<h1>{message}</h1>
</nav>
)
}
// Mount the functional component:
mount(<Title message='This is the Title!'/>, 'header')
Hydration with Mount
You use the mount
function to hydrate content rendered on the server. This means users will see content load faster. Hydration is performed by passing a third argument to mount
for the element you want to hydrate. Composi takes that element, creates a virtual node from it and uses that virtual node to patch the DOM.
import { h, mount } from 'composi'
// Hydrate a server-renered list:
function List(props) {
return (
{
props.data.map(item => - {item.value}
)
}
)
}
const items = ['One', 'Two', 'Three']
// There's a list rendered by the server with an id of #hydratable-list:
let list = mount(, 'section', '#hydratable-list')
Using hydration on server-rendered content allows you to embue it with events and dynamic behaviors with Composi. This means you page loads faster and it reaches interactivity faster.
Render
The render
function is used to update a mounted functional component. This is done with a reference to the mounted component that you saved, as illustrated in the first mount
example above. To use it, you will need to import it into your code:
import { h, mount, render } from 'composi'
render
takes three parameters:
vnode
: The virtual node returned when a component was mounted.tag
: the function or JSX tag used to mount the component.container
: the element in the DOM of a mounted functional component that you want to update
In the example below we are going to mount a component and then update it with the render
function using setTimeout
. Notice how we assign the mounted functional component to the title
variable:
import { h, mount } from 'composi'
// Define functional component:
function Title({message}) {
return(
<nav>
<h1>{message}</h1>
</nav>
)
}
// Store the mounted element in a variable:
const title = mount(<Title message='This is the Title!'/>, 'header')
// Update mounted component.
// Pass in `title` from above as second argument.
// Don't forget to reassign the result to `render`
// back to the component variable "title".
setTimeout(() => {
title = render(title, <Title message='A New Message Was Used.'/>, 'header')
}, 5000)
Using render
and passing a reference to the DOM tree lets us update the DOM tree in place.
Rendering Functional Components
Codepen Example:
See the Pen Composi render-1 by Robert Biggs (@rbiggs) on CodePen.
We could use the mount
function above to make several lists. To do that, we're going to use what's called a custom tag. This is a feature of JSX where when you define a functional component that begins with an uppercase letter, you can use it as a tag. Notice how we pass the CreateList
function to mount
as a JSX tag, <CreateList data={fruits} />
. At build time, Babel will convert that into a function that returns the virtual node for the render function to insert in the DOM.
Since we are using a custom JSX tag, the data that would normally be passed as a parameter to the functional component instead gets passed as a prop. That's the reason we inclosed the parameter data
in curly braces when we defined CreateList
. We used the term data
because the data is generic. Use whatever term makes sense for what you are using.
Codepen Example:
See the Pen Composi render-2 by Robert Biggs (@rbiggs) on CodePen.
In the above example, each subsequent call of the render
function will patch and update whatever was previously rendered. Be aware that the inital invocation of mount
will not replace any static content already in the container.
Gotchas
mount
always appends to the provided container. If you want to have server-side rendered content and replace it with new content, you'll need to use render
and pass it a reference to the server-rendered element. Notice in the following example how we query the DOM for the element and then pass it as the second argument of render
:
// In the index.html:
<nav class='header'>
<h1>Server-Rendered Content</h1>
</nav>
// JavaScript:
import { h, mount } from 'composi'
// Define functional component:
function Title({message}) {
return(
<nav class='header'>
<h1>{message}</h1>
</nav>
)
}
// Get reference to server-rendered nav at load time:
const header = document.querySelector('.header')
// Update server-rendered component.
// Pass in `header` from above as second argument:
setTimeout(() => render(<Title message='A New Message Was Used.'/>, header), 5000)
Hydration
You can use functional components to hydrate markup that was rendered on the server. Of course this needs to happen at load time. You can do this using the mount
function. Please read the API documentation for mount for more details about how to use it to hydrate server-rendered markup with functional components.
If you want to be able to create multiple instances of the same component, you can use mount
to inject the functional component into different containers while providing different data for each component, and then use the mount reference to update the functional components later with render
.
Unmount
The unmount
function allows you to remove the rendered component from the DOM. This does not effect the value of the reference returned by mount
and render
for that component.
To unmount a component, you pass unmount
the value returned by the mount
function.
After unmounting a component, you can use mount
to mount the component again. If the component has an onmount
lifecycle hook, this will also run again.
If your component has events attached with addEventListener
, you need to remove those with removeEventListener
before unmounting. Otherwise unmounting will create memory leaks that could eventually crash the browser. If you are using inline events, this is not a problem.
Here is a simplified example of mounting and unmounting and remounting to show how to do it:
import { h, mount, unmount } from 'composi'
function Title(props) {
return (
<nav>
<h1>Hello, {props.greet}!</h1>
</nav>
)
}
// We will use `title` to unmount later:
let title = mount(<Title greet='Joe' />, 'header')
// Unmount the component in two seconds:
setTimeout(() => {
unmount(title)
}, 2000)
// Remount the component in four seconds:
setTimeout(() => {
title = mount(<Title greet='Jill'/>, 'header')
}, 4000)
When to Use Unmount
In most cases you can handle whether a component renders or not using conditional logic. unmount
is for those rare cases where that is not an option. For more information about conditional rendering, visit this link.
Summary
Use mount
to inject a functional component into the DOM.
render
is similar to ReactDOM.render, allowing you to update a mounted component.
unmount
lets you remove a mounted component from the DOM, but conditional logic with mount
and render
is often a better choice.
mount
and render
make it easy to use functional components. However, class-based components offer more functionality and versitility. If you find you are having a hard time making a functional component do what you want, you should look at using a class component instead. You can learn more about them by reading the docs about how to extend the Component class.