자식 컴포넌트에서 update:modelValue 를 실행한 직후 props.modelValue를 출력하면 여전히 이전 값이 찍히는 현상이 있다. 부모의 이벤트 리스너는 분명 실행되었는데 자식이 받은 props는 바로 바뀌지 않는다.
// 자식 컴포넌트
function handleIncrease() {
const nextValue = props.modelValue + 1
console.log('before emit:', props.modelValue) // 0
emit('update:modelValue', nextValue)
console.log('after emit:', props.modelValue) // 여전히 0
}
emit() 호출하면 부모의 리스너 함수는 현재 실행 컨텍스트에서 즉시 동기적으로 실행된다. 부모 내부의 반응형 상태값도 그 시점에 바로 바뀐다.
하지만 부모 상태 변경으로 인한 컴포넌트 리렌더링과 자식 컴포넌트로의 새로운 props 전달은 Vue의 비동기 업데이트 큐에 담겨서 실행을 기다린다.
자식 이벤트 호출과 부모 상태 변경, 그리고 비동기 렌더링 큐 플러시의 동작 흐름이다.
자식의 자바스크립트 함수 handleIncrease 가 완전히 끝나기 전까지는 렌더링 배치 작업이 실행되지 않는다. 도중에 props.modelValue 를 다시 읽어도 아직 새 props를 받지 못해 이전 값 그대로 남아 있는 것이다.
올바른 상태 접근과 nextTick 활용
함수 안에서 다음 변경 값이 필요할 때는 이미 계산해 둔 로컬 변수를 직접 사용하는 편이 직관적이고 안전하다.
import { nextTick } from 'vue'
async function handleIncrease() {
const nextValue = props.modelValue + 1
emit('update:modelValue', nextValue)
// 1. 현재 함수 로직에서는 이미 계산한 nextValue를 바로 사용한다.
doSomething(nextValue)
// 2. 갱신된 DOM이나 자식에게 다시 전달된 props가 꼭 필요할 때만 nextTick을 기다린다.
await nextTick()
console.log('after nextTick:', props.modelValue) // 갱신된 값 (1)
}
갱신된 DOM 엘리먼트를 참조해야 하거나, 부모의 변경 사항이 완전히 반영된 props를 자식 함수 안에서 다시 사용해야할 할 때 nextTick 을 사용해 업데이트 플러시를 기다린다.
댓글 0개