What is the proper way to invoke render functions using Vue 3 composition API?

During my time with Vue 2, I would typically call render() in this manner:

export default {
    mounted(){
        ...
    },
    render(){
        ...
    },
    methods(){
        ...
    }
}

Now that I'm exploring Vue 3 and the composition API, I attempted to replicate the same approach. Here's what I experimented with:

export default {
    ...
    setup(props, context){
        ...
        const create_canvas = (h, id, props) => {
            _id.value = id
            _attrs.value = props.attrs
            return () => h('div', {
                class: `trading-vue-${id}`,
                style: {
                    left: props.position.x + 'px',
                    top: props.position.y + 'px',
                    position: 'absolute',
                }
            }, [
                h('canvas', Object.assign({
                    id: `${props.tv_id}-${id}-canvas`,
                    onmousemove: e => renderer.mousemove(e),
                    onmouseout: e => renderer.mouseout(e),
                    onmouseup: e => renderer.mouseup(e),
                    onmousedown: e => renderer.mousedown(e),
                    ref: 'canvas',
                    style: props.style,
                }, props.attrs))
            ].concat(props.hs || []))
        };

        function render() {
            const id = props.grid_id
            const layout = props.layout.grids[id]
            return () => create_canvas(h, `grid-${id}`, {
                position: {
                    x: 0,
                    y: layout.offset || 0
                },
                attrs: {
                    width: layout.width,
                    height: layout.height,
                    overflow: 'hidden'
                },
                style: {
                    backgroundColor: props.colors.back
                },
                hs: [
                    h(Crosshair, Object.assign(
                        common_props(),
                        layer_events
                    )),
                    h(KeyboardListener, keyboard_events),
                    h(UxLayer, {
                        id,
                        tv_id: props.tv_id,
                        uxs: uxs.value,
                        colors: props.colors,
                        config: props.config,
                        updater: Math.random(),
                        onCustomEvent: emit_ux_event
                    })
                ].concat(get_overlays(h))
            })
        };

        render()
    }
}

However, it seems like this code doesn't display anything in my template. I suspect that I might not be utilizing the render function correctly. Can someone assist me in understanding how to properly employ it?

Answer №1

Based on my understanding, the function h() is a concise way to generate vnodes and requires 3 parameters.

h(
    tag name,
    props/attributes,
    array of children
)

From what I gather, within the create_canvas function, you are attempting to create a div element with specified class and inline styles as attributes/props, and then adding a canvas element as a child to this div vnode. Therefore, instead of directly returning the vNode from setup(), it would be more appropriate to return a render function that in turn returns a vNode.

export default {
    props: {
      // props will be added here 
    },
    setup(props) {
        // render() { h(...) } ❌
           return () => {
               h('div', {
                class: `trading-vue-${id}`,
                style: {
                    left: props.position.x + 'px',
                    top: props.position.y + 'px',
                    position: 'absolute',
                }
            }, [
                h('canvas', Object.assign({
                    id: `${props.tv_id}-${id}-canvas`,
                    onmousemove: e => renderer.mousemove(e),
                    onmouseout: e => renderer.mouseout(e),
                    onmouseup: e => renderer.mouseup(e),
                    onmousedown: e => renderer.mousedown(e),
                    ref: 'canvas',
                    style: props.style,
                }, props.attrs))
            ].concat(props.hs || []))
       } ✅
    }
}

Answer №2

Instead of explicitly calling the render function, you simply declare it in Vue (and Vue will call it during rendering).

With the Composition API, all you need to do is return the render function from the setup method.

import { ref, h } from 'vue'

export default {
  props: {
    /* ... */
  },
  setup(props) {
    const count = ref(1)

    // return the render function
    return () => h('div', props.msg + count.value)
  }
}

When applying this concept to your own code, remember that the last line of the setup should be a return render() instead of just render() because the actual "render" function is returned by the render() function itself.


In JavaScript, functions can be treated as data - you can store and return them. The function is not immediately executed when stored or returned within another function, but rather created for later use. The caller of the "factory" function (in this case, the Vue framework) can save the reference to the returned function and decide when to execute it.

The Vue Composition API onMounted hook operates in a similar way. You pass a newly created function to onMounted(), which then stores the reference for Vue to call at a later point.

It's important to understand that the order of execution inside the setup() method does not matter since Vue controls when to call these functions. Vue will likely ensure that the render function is called at least once before any functions passed into onMounted() are executed (as the component must be rendered before being mounted).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the significance of "new" being omitted in the upcoming jQuery script?

After diving into the JQuery documentation, I discovered that it is possible to create the event object like this: //Instantiate a new jQuery.Event object without using the "new" keyword. var e = jQuery.Event( "click" ); I find it puzzling why we don&apo ...

Developing a digital sound system using JavaScript

I am currently in the process of developing a music player. Below is my HTML code: <div id="main"> <div id="list" draggable="true"> </div> <div id="player"> <div id="buttons"> < ...

Is it possible to use JavaScript to toggle mute and unmute functions on an iPad?

Seeking assistance with managing audio on an iPad using JavaScript (mute/unmute). Any suggestions or advice would be greatly appreciated. ...

Encountering an issue when trying to activate Vue UI: spawn cmd ENOENT error

Every time I run the 'vue ui' command in cmd, I encounter the following error message: events.js:288 throw er; // Unhandled 'error' event ^ Error: spawn cmd ENOENT at Process.ChildProcess._handle.onexit (internal/chil ...

Updating the state of an object nested within another object in React JS

So, I have a nested object within another object and I need to update the state of one or more properties in the inner object. This is my current state: const [products, setProducts] = useState({ name: '', type: '', price: '& ...

React/MaterialUI - Is there a way to customize the placeholder text for my multi-input field beyond the provided options?

I have a dropdown menu that accepts two JSON objects, symbol and company. export default function SymbolInput() { const [data, setData] = useState({ companies: [] }); const classes = useStyles(); useEffect(() => { Axios.get(" ...

Including a label for an array within an unnamed array in a JSON string

Is there a way to transform the following data: [{"name": "Donald"}, {"name": "George"}] Into this format instead: {MyArray: [{"name": "Donald"}, {"name": "George"}]} I am currently working on a database server that I built using node.js, express, an ...

Determine the date and time based on the number of days passed

Hey there! I have a dataset structured like this: let events = { "KOTH Airship": ["EVERY 19:00"], "KOTH Castle": ["EVERY 20:00"], Totem: ["EVERY 17:00", "EVERY 23:00"], Jum ...

Concealing items in a loop using Javascript

I need assistance with this issue. I am trying to hide text by clicking on a link, but it doesn't seem to be working correctly. Even though I tried the following code, I can't figure out why it's not functioning as expected. Is there a diff ...

Toggle the visibility of table rows using checkboxes

I'm working with checkboxes to toggle the visibility of specific rows in a table based on their content matching the selected checkbox values. Checkboxes: <input type='checkbox' name='foo1' value='foo1' v-model="sele ...

What is the best way to recover accented characters in Express?

Encountering issues with accented characters in my POST request .. I attempted using iconv without success.. Snippet of my code: Edit: ... var bodyString = JSON.stringify(req.body); var options = { host: XXXXX, port: XXX, ...

bespoke HTML elements and properties

I'm currently facing some difficulties and I am unsure of how challenging it can be. I have tried following various tutorials, including those on SO and YouTube, but they all provide different approaches leaving me stuck. My goal is to create a custo ...

Display the table once the radio button has been selected

Before we proceed, please take a look at the following two images: image 1 image 2 I have over 20 fields similar to 'Image 1'. If "Yes" is selected, then a table like in 'Image 2' should be displayed. This means I have 20 Yes/No fields ...

How to apply props conditionally in VueJS?

I have a component called blogPost (Component A) that is imported in another component named B. When a button in Component B is clicked, Component A opens in a modal. There are two different use cases for this scenario. 1. One case requires passing 2 prop ...

What could be causing me to receive null when trying to retrieve an element with document.getElementById, even after invoking $(document).ready(function() { ... })?

Here's a small example. I'm feeling a bit rusty with JavaScript, as it's been some time since I last worked with it. The problem I'm encountering is an error in Chrome developer tools: "Uncaught TypeError: Cannot set property 'src ...

Experience seamless one-to-many broadcasting with WebRTC/Kurento, featuring server-side recording capabilities

I am currently exploring Kurento to determine if it fits my needs. I am interested in developing a mobile application that can record and stream video to a server in real-time, with the server saving the video on its file system as it is being transmitted. ...

Is there a way for me to modify this carousel so that it only stops when a user hovers over one of the boxes?

I am currently working to modify some existing code to fit my website concept. One aspect I am struggling with is how to make the 'pause' function activate only when a user hovers over one of the li items, preventing the carousel from looping end ...

Implementing an onclick function to initiate a MySQL update query

<?php include_once("db.php"); $result=mysql_query("SELECT * FROM stu WHERE receiver='DM4'"); while($row=mysql_fetch_array($result)){ echo "<tr>"; echo "<td>" . $row['ptype'] . "</td>"; echo "<td>" . $row[&apo ...

Create a graphical representation of network topology using VueJS

I am in the process of creating a network topology diagram using vueJS and v-network-graph library. Here is an example of the current graph I have achieved: https://i.stack.imgur.com/pTrtW.png What I'm looking to do is add icons on each link betwee ...

The Ejs page is failing to render on the simplified code version

Here is the code that displays the 'post' page: app.get("/posts/:postName", function(req, res) { const requestedTitle = _.lowerCase(req.params.postName); posts.forEach(function(post) { const storedTitle = _.lowerCase(post.title ...