v-for와 v-if를 같이 썼더니 property is not defined 오류가 난 이유
문제 발생
완료되지 않은 할 일만 그리려고 두 디렉티브를 한 요소에 붙였습니다.
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>화면에는 아무것도 나오지 않고 콘솔에 오류가 찍혔습니다.
Property "todo" was accessed during render but is not defined on instance.원인 분석
평가 순서 때문입니다. Vue 문서가 그대로 적어둡니다 — 같은 노드에 있을 때 v-if가 v-for보다 우선순위가 높습니다. 즉 v-if 조건은 v-for 스코프의 변수에 접근할 수 없습니다.
v-if가 먼저 실행되는 시점에는 아직 반복이 시작되지 않아 todo라는 변수가 없습니다. 컴포넌트 인스턴스에 그런 이름이 없으니 위 오류가 납니다. Vue 2에서는 반대 순서였기 때문에 예전 코드를 옮겨오면 이 자리에서 걸립니다.
문서의 결론도 명시적입니다 — 암묵적인 우선순위 때문에 v-if와 v-for를 같은 요소에 쓰는 것은 권장되지 않습니다.
해결 방안
- 필터링이 목적이면
computed로 걸러진 목록을 만듭니다. 문서가 첫 번째로 제시하는 방법이고, 대개 이게 맞습니다.
<script setup>
const activeTodos = computed(() => todos.value.filter((todo) => !todo.isComplete));
</script>
<template>
<li v-for="todo in activeTodos" :key="todo.id">{{ todo.name }}</li>
</template>렌더 함수가 매번 filter를 돌지 않고 캐시된다는 이득도 함께 얻습니다.
- 정말 반복 안에서 조건이 필요하면
<template v-for>로 감쌉니다. 문서가 제시하는 다른 방법입니다.
<template v-for="todo in todos" :key="todo.id">
<li v-if="!todo.isComplete">{{ todo.name }}</li>
</template>v-for가 바깥으로 나가서 todo가 안쪽 v-if 스코프에 들어옵니다. key는 <template> 쪽에 둡니다.
- "목록 전체를 보일지 말지"라면 컨테이너에
v-if를 답니다. 항목마다 조건을 평가할 이유가 없습니다.
<ul v-if="todos.length">
<li v-for="todo in todos" :key="todo.id">{{ todo.name }}</li>
</ul>- 오류 메시지를 그대로 믿지 않습니다. "not defined on instance"는 오타를 의심하게 만들지만, 이 경우 원인은 이름이 아니라 평가 순서입니다.
v-for와v-if가 한 줄에 같이 있으면 그것부터 봅니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.