i have a vue component using the composition API that has some props
passed into it.
i would like this component to pass its props into a composable that's a wrapper around useFetch
, and i would like the composable to retain reactivity when the component's props change. i.e. i want useFetch
to re-run when the props change.
the following works when i explicitly pass the props in as refs to the composable.
component:
<script setup lang="ts">
const props = defineProps({
page: {
default: 1,
required: false,
type: Number,
},
search: {
required: true,
type: String,
},
});
const { data, error, pending } = await useGetData(toRef(props, 'page'), toRef(props, 'search'));
</script>
composable:
export default (page: Ref<number>, search: Ref<string>) => {
const limit = ref(20);
const offset = computed(() => {
const p = page.value || 1;
return (p > 1) ? ((p - 1) * limit.value) : 0;
});
return useLazyFetch<DataIndex>('/api/data', {
query: {
limit,
offset,
search,
},
deep: false,
});
});
now, i would like to pass the props to my composable as an object instead of an ordered set of arguments. when i do this i seem to lose reactivity on the props - i.e. the query doesn't re-run when the props change in my component.
i've tried various interations of attempting to pass the props in as refs, but have had no luck.
component:
<script setup lang="ts">
const props = defineProps({
page: {
default: 1,
required: false,
type: Number,
},
search: {
required: true,
type: String,
},
});
const dataInput = reactive({
page: props.page,
search: props.search,
});
const { data, error, pending } = await useGetData(toRefs(dataInput));
</script>
composable:
interface GetDataInput {
page?: Ref<number>
search?: Ref<string>
};
export default (input: GetDataInput) => {
const limit = ref(20);
const offset = computed(() => {
const p = input.page?.value || 1;
return (p > 1) ? ((p - 1) * limit.value) : 0;
});
return useLazyFetch<DataIndex>('/api/data', {
query: {
limit,
offset,
search: input.search,
},
deep: false,
});
});
i assume that i'm losing reactivity in this example because either dataInput
is not seeing the prop change, or because the input
argument itself is not reactive when passed into the composable.
what am i missing here?
Your problem is using defineProps
in the wrong way.
defineProps
will return a reactive object (a Proxy
), means props
is now reactive. Since you declare a new reactive
with your props.x
. dataInput
is a new reactive object that is initialized with value of props.x
(just value initialization, no reactivity here)
That's why when props
is changed, dataInput
is kept intact cause they are not related or connected. Try to use toRefs(props)
instead
<script setup lang="ts">
const props = defineProps({
page: {
default: 1,
required: false,
type: Number,
},
search: {
required: true,
type: String,
},
});
//const dataInput = reactive({
// page: props.page,
// search: props.search,
//});
const { data, error, pending } = await useGetData(toRefs(props));
</script>
And for some reason, if you still want to keep dataInput
, try this instead
<script setup lang="ts">
const props = defineProps({
page: {
default: 1,
required: false,
type: Number,
},
search: {
required: true,
type: String,
},
});
const dataInput = reactive(toRefs(props));
const { data, error, pending } = await useGetData(toRefs(dataInput));
</script>