Vue 3: resolveComponent can only be used in render() or setup()

17.1k views Asked by At

I'm trying to render a template in Vue 3. The template contains a component, which is locally registered on the instance.

import template from './template'
import RenderlessPagination from "./RenderlessPagination";
import {h, resolveComponent} from 'vue'

export default {
    name: 'Pagination',
    components: {RenderlessPagination},
    provide() {
        return {
            Page: () => this.value,
            perPage: () => this.perPage,
            records: () => this.records
        }
    },
    render() {
        const RLPagination = resolveComponent('renderless-pagination');

        return h(RLPagination, {
            slots: {
                default: function (props) {
                    return props.override ? h(
                        props.override,
                        {
                            attrs: {props}
                        }
                    ) : template(props)(h)
                }
            }
        })
    },
    props: {
        value: {
            type: Number,
            required: true,
            validator(val) {
                return val > 0;
            }
        },
        records: {
            type: Number,
            required: true
        },
        perPage: {
            type: Number,
            default: 25
        },
        options: {
            type: Object
        }
    },
    data: function () {
        return {
            aProps: {
                role: "button"
            }
        }
    }
}

This results in a following run-time warning:

resolveComponent can only be used in render() or setup()

In addition the renderless-pagination component is not compiled, but just added as-is to the HTML:

<renderless-pagination slots=[Object Object]></renderless-pagination>

As you can see I am using resolveComponent in the render function, which makes me wonder why it complains, and why the renderless-pagination component is not compiled.

I checked the value returned from the resolveComponent call, and it just returns the component's name, i.e renderless-pagination.

What am I missing here?

EDIT:

Following @skirtle's comment I have removed the duplicate Vue instance from the package, using the externals option in webpack. However, I am still receiving the same error:

resolveComponent can only be used in render() or setup()

Relavant webpack config:

  output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'vue-pagination-2.min.js',
        library:'Pagination',
        libraryTarget: "commonjs"
    },
    externals: {
        vue: {
            root: 'Vue',
            commonjs: 'vue',
            commonjs2: 'vue',
            amd: 'vue'
        }
    }
2

There are 2 answers

0
Matanya On

Thanks to @skirtle's comment I have removed the duplicate Vue instance but was still getting the same error.

What ended up working is passing the component instance itself to the h function, like so:

render() {
        return h(RenderlessPagination, {}, {
                default: function (props) {
                    return props.override ? h(
                        props.override,
                        {
                            props
                        }
                    ) : template(props)(h)
                }
        })

I suppose that's what resolveComponent does behind the scenes... Oh,well

0
shunyue On

vue.config.js

add

module.exports = {
    ...
    configureWebpack:{
        resolve:{
            alias:{
               vue: path.resolve('./node_modules/vue')
            }
        }
    }
}