🫠 DOM에 접근하려고 하는 상황에서 분명 코드 상으로는 맞는 것 같은데 DOM이 비동기적으로 렌더링 돼서 접근이 제대로 안될때 . . .❗
✨ 즉, 코드 상에서 바뀐 데이터를 DOM에도 즉각적으로 반영하고 싶을 때
바로 코드를 통해 살펴보도록 하자...
<script setup>
import { ref } from "vue";
import Empty from "./Empty.vue";
import Observer from "./Observer.vue";
const isLoading = ref(false);
const items = ref([]);
const root = ref(null);
const fetch = async () => {
isLoading.value = true;
const result = await store.dispatch(~~~);
isLoading.value = false;
if (result?.success) {
items.value = [...result.data.results];
}
};
fetch();
</script>
<template>
<div class="container">
<Empty v-if="isLoading />
<div v-else class="root">
<Community
v-for="(community, index) in items"
:key="index"
/>
<Observer :root='root' />
</div>
</div>
</template>
위 코드를 보면 로딩중일 때 (isLoading이 true일 때) 화면에 Empty 컴포넌트를 표시하고 로딩이 완료 됐을 때 Community 컴포넌트를 표시한다. 처음에 fetch()가 실행되어 데이터를 불러오는 과정이 끝나면 isLoading이 false가 된다.
이때 Observer 컴포넌트의 props로 <div v-else class="root">를 전달하고 싶다.
그러면 <div v-else class="root">에 어떻게 접근해야 할까...❓
onMounted(() => {
root.value = document.querySelector(".root");
console.log("root", root.value);
});
root 요소가 렌더링 되기 위해서는 isLoading 값이 false여야 하는데 onMounted는 fetch()에서 데이터를 불러오는 과정이 완료되어 isLoading 값이 false가 되기 전에 실행되어 root가 null로 출력된다.
watch(isLoading, (isLoading) => {
if (!isLoading) {
root.value = document.querySelector(".root");
console.log("root", root.value);
}
});
isLoading 값에 watch를 걸어 isLoading 값이 false일 때 root 요소에 접근하려고 했으나 또 다시 null이 출력된다. isLoading이 false가 될 때까지 기다렸지만 실패한 이유는 Vue가 DOM 업데이트를 비동기적으로 하기 때문에 DOM이 그려지기 전에 root 요소에 접근했기 때문이다.
이러한 상황에서 사용하는게 nextTick이다..
watch(isLoading, (isLoading) => {
if (!isLoading) {
nextTick(() => {
root.value = document.querySelector(".root");
console.log("root", root.value);
});
}
});
드디어 성공!!😊
isLoading 값이 false로 변경된 후 nextTick을 사용해서 Vue가 DOM 업데이트를 마쳤을 때 root 요소에 접근하도록 하면 내가 원했던대로 동작한다.