Vue3 Reactive Data Not Updating View After API Assignment
Problem Scenario
When defining data with reactive and assigning values through API calls, the page fails to reflect the updated values:
interface FormData {
englishAddress: string;
chineseAddress: string;
englishCompany: string;
chineseCompany: string;
englishContact: string;
chineseContact: string;
englishDesigner: string;
chineseDesigner: string;
itemCount: number;
phoneNumber: string;
email: string;
projectNames: Array<any>;
siteUrl: string;
}
const formReference = ref<FormInstance>();
let formData = reactive<FormData>({
englishAddress: '',
chineseAddress: '',
englishCompany: '',
chineseCompany: '',
englishContact: '',
chineseContact: '',
englishDesigner: '',
chineseDesigner: '',
itemCount: 1,
phoneNumber: '',
projectNames: [{ value: '' }],
siteUrl: '',
});
The issue occurs when attempting to populate this reactive object after an API call:
const loadDetails = async () => {
try {
const response = await fetchPersonalDetails(route.query.id);
const { status, data } = response;
if (status === SUCCESS_CODE) {
const transformedProjects = data.projectNames.map(name => ({ value: name }));
// This approach breaks reactivity
formData = {
...data,
projectNames: transformedProjects,
};
}
} catch (error) {
console.error('Failed to load details:', error);
}
};
onMounted(() => {
if (route.query.id) {
loadDetails();
}
});
Solution
Instead of direct object assignment which breaks the reactive connection, use Object.assign to preserve the proxy relationship:
const loadDetails = async () => {
try {
const response = await fetchPersonalDetails(route.query.id);
const { status, data } = response;
if (status === SUCCESS_CODE) {
const transformedProjects = data.projectNames.map(name => ({ value: name }));
Object.assign(formData, {
...data,
projectNames: transformedProjects,
});
}
} catch (error) {
console.error('Failed to load details:', error);
}
};
How It Works
The Object.assign method works because it modifies the properties of the existing reactive object rather than replacing the entire object. Vue 3's reactive function creates reactive objects using JavaScript Proxies to track property changes.
When you directly reassign the entire object (formData = {...}), you break the proxy relationship, causing Vue to lose track of the new object's changes. The Proxy-based reactivity system can no longer detect modifications to the new object reference.
Using Object.assign avoids this problem by updating individual properties of the existing object without changing its reference, thus preserving the proxy relatinoship and maintaining reactivity.
Key Points:
- Maintains Proxy Connection: Objects created with
reactiveuse proxies for tracking. Direct reassignment destroys this connection, whileObject.assignpreserves it by modifying properties in place. - Batch Property Updates:
Object.assignallows updating multiple properties simultaneously while ensuring Vue can track all changes effectively. - Preserves Reactivity: Vue detects property changes through the proxy object. As long as modifications occur on the same proxy instance, the view updates automatically.