In the previous Riot.js versions you could just extend to your component instance via keyword in your <script> tags. This syntax sugar was removed in favor of a cleaner API relying on the standard javascript ES2018 syntax:

    old

    new

    1. <my-component>
    2. <p onclick={onClick}>{message}</p>
    3. <!-- mandatory <script> tag -->
    4. <script>
    5. export default {
    6. onClick() {
    7. this.message = 'hello'
    8. this.update()
    9. }
    10. }
    11. </script>
    12. </my-component>

    In this way your editor, and other compilers like typescript will not get confused by your components javascript logic and can be used along the way without any special concerns.

    It’s worth to mention that this change was driven by the new :

    Notice how the use of the <script> tag becomes mandatory to split your components templates from their javascript logic.

    Template shortcuts

    The template shortcuts were completely removed in favor of pure and more explicit javascript expressions. Let’s see how it’s simple to achieve the same results in a cleaner way in Riot.js 4.

    old

    1. <my-component>
    2. <p class={ active: isActive, disabled: isDisabled }>hello</p>
    3. </my-component>

    new

    1. // riot-class-names-plugin.js
    2. /**
    3. * Convert object class constructs into strings
    4. * @param {Object} classes - class list as object
    5. * @returns {string} return only the classes having a truthy value
    6. */
    7. function classNames(classes) {
    8. return Object.entries(classes).reduce((acc, item) => {
    9. const [key, value] = item
    10. if (value) return [...acc, key]
    11. return acc
    12. }, []).join(' ')
    13. }
    14. // install the classNames plugin
    15. riot.install(function(component) {
    16. // add the classNames helper to all the riot components
    17. component.classNames = classNames
    18. return component
    19. })
    1. <my-component>
    2. <p class={
    3. classNames({
    4. active: isActive,
    5. disabled: isDisabled
    6. })
    7. }>hello</p>
    8. </my-component>

    Even better, you can use the classNames directly in your components logic keeping your templates clean avoiding the use of riot.install for example:

    1. <my-component>
    2. <p class={getParagraphClasses()}>hello</p>
    3. <script>
    4. import classNames from 'classnames'
    5. export default {
    6. getParagraphClasses() {
    7. return classNames({
    8. active: this.isActive,
    9. disabled: this.isDisabled
    10. })
    11. }
    12. }
    13. </script>
    14. </my-component>

    old

    1. <my-component>
    2. <p style={{ color: 'red', 'font-size': `${fontSize}px` }}>hello</p>
    3. </my-component>

    new

    1. <my-component>
    2. <p style={
    3. color: 'red',
    4. 'font-size': `${fontSize}px`
    5. })
    6. }>hello</p>
    7. </my-component>

    Now your code and your helpers will be completely customizable without relying on the framework built in.This means that you will have more freedom and less bugs coming from your third party code…and don’t forget to remember that:

    …Explicit is better than implicit…

    The Observable pattern was completely integrated into the previous Riot.js versions. This was an opinionated decision that might not work for all users. Riot.js 3 leaves you the decision regarding which programming pattern to use in your application and for this reason the observable helpers were completely removed from the source code in favor of a more generic approach.

    old

    1. <my-component>
    2. <p>{message}</p>
    3. this.on('mount', function() {
    4. this.message = 'hello'
    5. this.update()
    6. })
    7. </my-component>

    new

    1. <my-component>
    2. <p>{message}</p>
    3. <script>
    4. export default {
    5. onMounted() {
    6. this.message = 'hello'
    7. this.update()
    8. }
    9. }
    10. </script>
    11. </my-component>

    Please check also the new components .

    This change opens many new possibilities to manage your application state keeping the doors open to the nostalgic users that still prefer the observable lifecycle pattern.

    1. // riot-observable-plugin.js
    2. import observable from 'riot-observable'
    3. /**
    4. * Trigger the old observable and the new event
    5. * @param {RiotComponent} component - riot component
    6. * @param {Function} callback - lifecycle callback
    7. * @param {string} eventName - observable event name
    8. * @param {...[*]} args - event arguments
    9. * @returns {RiotComponent|undefined}
    10. */
    11. function triggerEvent(component, callback, eventName, ...args) {
    12. component.trigger(eventName, ...args)
    13. return callback.apply(component, [...args])
    14. }
    15. riot.install(function(componentAPI) {
    16. const {
    17. onBeforeMount,
    18. onMounted,
    19. onBeforeUpdate,
    20. onUpdated,
    21. onBeforeUnmount,
    22. onUnmounted
    23. } = componentAPI
    24. // make the riot component observable
    25. const component = observable(componentAPI)
    26. // remap the new event to the old ones
    27. const eventsMap = {
    28. onBeforeMount: ['before-mount', onBeforeMount],
    29. onMounted: ['mount', onMounted],
    30. onBeforeUpdate: ['before-update', onBeforeUpdate],
    31. onUpdated: ['updated', onUpdated],
    32. onBeforeUnmount: ['before-unmount', onBeforeUnmount],
    33. onUnmounted: ['unmount', onUnmounted]
    34. }
    35. Object.entries(eventsMap).forEach(([eventName, value]) => {
    36. const [oldObservableEvent, newCallback] = value
    37. component[eventName] = (...args) => triggerEvent(
    38. component, newCallback, oldObservableEvent, ...args
    39. )
    40. })
    41. return component
    42. })
    1. <my-component>
    2. <p>{message}</p>
    3. <script>
    4. export default {
    5. onMounted() {
    6. console.log('I got updated!')
    7. })
    8. }
    9. }
    10. </script>
    11. </my-component>

    Opts vs props and state

    The previous Riot.js versions provided the opts key to each component. This key was renamed props and it becomes immutable: it’s a read only property frozen via Object.freeze.The props object can be only updated outside of the component that reads from it, while the new state object is updated via .

    old

    1. <my-component>
    2. </my-component>

    The ref attributes were replaced by the $ and $$ component helpers preferring a functional approach over mutable properties.

    old

    1. <my-component>
    2. <p ref='paragraph'>{message}</p>
    3. this.on('mount', function() {
    4. this.refs.paragraph.innerHTML = '<b>hello</b>'
    5. })
    6. </my-component>

    new

    1. <my-component>
    2. <p>{message}</p>
    3. <script>
    4. export default {
    5. onMounted() {
    6. const paragraph = $('p')
    7. paragraph.innerHTML = '<b>hello</b>'
    8. }
    9. }
    10. </script>
    11. </my-component>

    The new helpers will never return the children component instances but only DOM nodes

    Parent and children

    The parent and tags keys were heavily abused by Riot.js users. They were the source of many side effects and clear bad practice. For this reason the children/parent components created via Riot.js 4 never expose their internal API, components communicate only via props and don’t interact directly with the external world.

    old

    1. <my-component>
    2. <my-child ref='child'/>
    3. this.on('mount', function() {
    4. this.refs.child.update({ message: 'hello' })
    5. })
    6. </my-component>

    new

    1. <my-component>
    2. <my-child message={childMessage}/>
    3. <script>
    4. export default {
    5. onMounted() {
    6. this.childMessage = 'hello'
    7. this.update()
    8. }
    9. }
    10. </script>
    11. </my-component>

    You can of course write your own riot plugin to add the ref behaviour but it’s highly not recommended:

    1. // riot-ref-plugin.js
    2. riot.install(function(component) {
    3. const { onBeforeMount } = component
    4. component.onBeforeMount = (props, state) => {
    5. if (props.ref) {
    6. props.ref(component)
    7. }
    8. onBeforeMount.apply(component, [props, state])
    9. }
    10. return component
    11. })
    1. <my-component>
    2. <my-child ref={onChildRef}/>
    3. <script>
    4. export default {
    5. onChildRef(child) {
    6. this.child = child
    7. },
    8. onMounted() {
    9. this.child.update()
    10. }
    11. }
    12. </script>
    13. </my-component>

    Custom events dispatching

    Listening custom events dispatched in child components can be done simply via properties:

    1. <my-child>
    2. <figure>
    3. <img src="path/to/the/image.jpg" onloaded={props.onLoaded}/>
    4. </my-child>

    The <virtual> tag was removed and it can’t be used anymore. Please use the <template> tag instead

    Yield tags

    The <yield> tags were replaced by the <slot>s having a more predictable behavior. Please check the to understand how they work.