Mounting

Once a component is created you can mount it on the page as follows:

  1. <body>
  2. <!-- place the custom component anywhere inside the body -->
  3. <my-component></my-component>
  4. <!-- is attributes are also supported -->
  5. <div is="my-component"></div>
  6. <!-- include riot.js -->
  7. <script src="riot.min.js"></script>
  8. <!-- mount the component -->
  9. <script type="module">
  10. // import the component javascript output generated via @riotjs/compiler
  11. import MyComponent from './my-component.js'
  12. // register the riot component
  13. riot.register('my-component', MyComponent)
  14. riot.mount('my-component')
  15. </script>
  16. </body>

Custom components inside the body of the page needs to be closed normally: <my-component></my-component> and self-closing: <my-component/> is not supported.

Some example uses of the mount method:

  1. // mount an element with a specific id
  2. riot.mount('#my-element')
  3. // mount selected elements
  4. riot.mount('todo, forum, comments')

A document can contain multiple instances of the same component.

Accessing DOM elements

Riot gives you access to your component DOM elements via this.$ and this.$$ helper methods.

  1. <my-component>
  2. <h1>My todo list</h1>
  3. <ul>
  4. <li>Learn Riot.js</li>
  5. <li>Build something cool</li>
  6. </ul>
  7. <script>
  8. export default {
  9. onMounted() {
  10. const title = this.$('h1') // single element
  11. const items = this.$$('li') // multiple elements
  12. }
  13. }
  14. </script>
  15. </my-component>

How to use jQuery, Zepto, querySelector, etc.

If you need to access the DOM inside Riot, you’ll want to take a look at the riot component lifecycle. Notice that the DOM elements aren’t instantiated until the mount event first fires, meaning any attempt to select an element before then will fail.

  1. <my-component>
  2. <p id="findMe">Do I even Exist?</p>
  3. <script>
  4. var test1 = document.getElementById('findMe')
  5. console.log('test1', test1) // Fails
  6. export default {
  7. onMounted() {
  8. const test2 = document.getElementById('findMe')
  9. console.log('test2', test2) // Succeeds, fires once (per mount)
  10. },
  11. onUpdated() {
  12. const test3 = document.getElementById('findMe')
  13. console.log('test3', test3) // Succeeds, fires on every update
  14. }
  15. }
  16. </script>
  17. </my-component>

Contexted DOM query

Now that we know how to get DOM elements in the onUpdated or onMounted callbacks, we can make this useful by also adding a context to our element queries to the root element (the riot tag we’re creating).

  1. <my-component>
  2. <p id="findMe">Do I even Exist?</p>
  3. <p>Is this real life?</p>
  4. <p>Or just fantasy?</p>
  5. <script>
  6. export default {
  7. onMounted() {
  8. // Contexted jQuery
  9. $('p', this.root) // similar to this.$
  10. // Contexted Query Selector
  11. this.root.querySelectorAll('p') // similar to this.$$
  12. }
  13. }
  14. </script>
  15. </my-component>

Properties

You can pass initial properties for components in the second argument

  1. <script>
  2. riot.mount('todo', { title: 'My TODO app', items: [ ... ] })
  3. </script>

The passed data can be anything, ranging from a simple object to a full application API. Or it can be a Redux store. Depends on the designed architecture.

Inside the tag the properties can be referenced with the this.props attribute as follows:

  1. <my-component>
  2. <!-- Props in HTML -->
  3. <h3>{ props.title }</h3>
  4. <script>
  5. export default {
  6. onMounted() {
  7. // Props in javascript
  8. const title = this.props.title
  9. // this.props is frozen and it's immutable
  10. this.props.description = 'my description' // this will not work
  11. }
  12. }
  13. </script>
  14. </my-component>

State

Each riot component can use the this.state object to store or modify its internal state.While the this.props attribute is frozen the this.state object is completely mutable and it could be updated manually or via the this.update() method:

  1. <my-component id="{ state.name }-{ state.surname }">
  2. <p>{ state.name } - { state.surname }</p>
  3. <script>
  4. export default {
  5. onMounted() {
  6. // this is good but doesn't update the component DOM
  7. this.state.name = 'Jack'
  8. // this call updates the state and the component DOM as well
  9. this.update({
  10. surname: 'Black'
  11. })
  12. }
  13. }
  14. </script>
  15. </my-component>

Riot component lifecycle

A component is created in following sequence:

  • The component object is created
  • The javascript logic is executed
  • All HTML expressions are calculated
  • The component DOM is mounted on the page and “onMounted” callback is calledAfter the component is mounted the expressions are updated as follows:

  • When this.update() is called on the current component instance

  • When this.update() is called on a parent component, or any parent upwards. Updates flow uni-directionally from parent to child.The “onUpdated” callback is called every time component tag is updated.

Since the values are calculated before mounting there are no surprise issues such as failed <img src={ src }> calls.

Lifecycle callbacks

You can setup you component lifecycles as follows:

  1. <my-component>
  2. <script>
  3. export default {
  4. onBeforeMount(props, state) {
  5. // before the component is mounted
  6. },
  7. onMounted(props, state) {
  8. // right after the component is mounted on the page
  9. },
  10. onBeforeUpdate(props, state) {
  11. // allows recalculation of context data before the update
  12. },
  13. onUpdated(props, state) {
  14. // right after the component template is updated after an update call
  15. },
  16. onBeforeUnmount(props, state) {
  17. // before the component is removed
  18. },
  19. onUnmounted(props, state) {
  20. // when the component is removed from the page
  21. }
  22. }
  23. </script>
  24. </my-component>

Any callback receives always the current this.props and this.state as arguments.