Skip to content

Commit

Permalink
fix(hmr): force full update on nested child components (#1312)
Browse files Browse the repository at this point in the history
  • Loading branch information
pikax authored Jun 12, 2020
1 parent 4492b88 commit 8f2a748
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 13 deletions.
71 changes: 71 additions & 0 deletions packages/runtime-core/__tests__/hmr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,75 @@ describe('hot module replacement', () => {
rerender(parentId, compileToFunction(`<Child msg="bar" />`))
expect(serializeInner(root)).toBe(`<div>bar</div>`)
})

// #1305 - component should remove class
test('remove static class from parent', () => {
const root = nodeOps.createElement('div')
const parentId = 'test-force-class-parent'
const childId = 'test-force-class-child'

const Child: ComponentOptions = {
__hmrId: childId,
render: compileToFunction(`<div>child</div>`)
}
createRecord(childId, Child)

const Parent: ComponentOptions = {
__hmrId: parentId,
components: { Child },
render: compileToFunction(`<Child class="test" />`)
}
createRecord(parentId, Parent)

render(h(Parent), root)
expect(serializeInner(root)).toBe(`<div class="test">child</div>`)

rerender(parentId, compileToFunction(`<Child/>`))
expect(serializeInner(root)).toBe(`<div>child</div>`)
})

test('rerender if any parent in the parent chain', () => {
const root = nodeOps.createElement('div')
const parent = 'test-force-props-parent-'
const childId = 'test-force-props-child'

const numberOfParents = 5

const Child: ComponentOptions = {
__hmrId: childId,
render: compileToFunction(`<div>child</div>`)
}
createRecord(childId, Child)

const components: ComponentOptions[] = []

for (let i = 0; i < numberOfParents; i++) {
const parentId = `${parent}${i}`
const parentComp: ComponentOptions = {
__hmrId: parentId
}
components.push(parentComp)
if (i === 0) {
parentComp.render = compileToFunction(`<Child />`)
parentComp.components = {
Child
}
} else {
parentComp.render = compileToFunction(`<Parent />`)
parentComp.components = {
Parent: components[i - 1]
}
}

createRecord(parentId, parentComp)
}

const last = components[components.length - 1]

render(h(last), root)
expect(serializeInner(root)).toBe(`<div>child</div>`)

rerender(last.__hmrId!, compileToFunction(`<Parent class="test"/>`))
expect(serializeInner(root)).toBe(`<div class="test">child</div>`)
})
})
19 changes: 11 additions & 8 deletions packages/runtime-core/src/componentRenderUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,13 @@ export function shouldUpdateComponent(
// Parent component's render function was hot-updated. Since this may have
// caused the child component's slots content to have changed, we need to
// force the child to update as well.
if (
__DEV__ &&
(prevChildren || nextChildren) &&
parentComponent &&
parentComponent.hmrUpdated
) {
return true
if (__DEV__ && (prevChildren || nextChildren) && parentComponent) {
let parent: ComponentInternalInstance | null = parentComponent
do {
if (parent.hmrUpdated) {
return true
}
} while ((parent = parent.parent))
}

// force child update for runtime directive or transition on component vnode.
Expand All @@ -268,8 +268,11 @@ export function shouldUpdateComponent(
return true
}
if (patchFlag & PatchFlags.FULL_PROPS) {
if (!prevProps) {
return !!nextProps
}
// presence of this flag indicates props are always non-null
return hasPropsChanged(prevProps!, nextProps!)
return hasPropsChanged(prevProps, nextProps!)
} else if (patchFlag & PatchFlags.PROPS) {
const dynamicProps = nextVNode.dynamicProps!
for (let i = 0; i < dynamicProps.length; i++) {
Expand Down
17 changes: 12 additions & 5 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,11 +791,18 @@ function baseCreateRenderer(
invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate')
}

if (__DEV__ && parentComponent && parentComponent.hmrUpdated) {
// HMR updated, force full diff
patchFlag = 0
optimized = false
dynamicChildren = null
// check if any component of the parent chain has `hmrUpdated`
if (__DEV__ && parentComponent) {
let parent: ComponentInternalInstance | null = parentComponent
do {
if (parent.hmrUpdated) {
// HMR updated, force full diff
patchFlag = 0
optimized = false
dynamicChildren = null
break
}
} while ((parent = parent.parent))
}

if (patchFlag > 0) {
Expand Down

0 comments on commit 8f2a748

Please sign in to comment.