Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redraws called from a didRecycle hook are ignored. #247

Open
claytondol opened this issue Mar 22, 2023 · 1 comment
Open

Redraws called from a didRecycle hook are ignored. #247

claytondol opened this issue Mar 22, 2023 · 1 comment

Comments

@claytondol
Copy link

claytondol commented Mar 22, 2023

When a redraw is called and didRecycle is triggered on an element, if didRecycle subsequently makes changes and calls another redraw, the second redraw is ignored and changes aren't reflected until something else triggers another redraw.

Minimal example demonstrating issue: https://jsfiddle.net/2j3phtag/. Here you'll see that the recycle lags one behind the main counter, until the view is updated apart from the recycle hook.

The use-case we have is that when a node is recycled its required size may overflow the bounds of its container, in which case we activate scroller buttons. If we do so, we need to trigger another redraw with the buttons now visible. We expected that just calling redraw would be sufficient.

@lawrence-dol
Copy link
Collaborator

lawrence-dol commented Mar 22, 2023

I looked into this with Clay yesterday, and it seems to me that the drainQueue function is where the problem lies. Redraws during other hooks, and during render functions are also affected (for example, didRedraw, below).

	function drainQueue() {
		redrawQueue.forEach(vm => {
			// don't redraw if vm was unmounted
			if (vm.node == null)
				return;

			// don't redraw if an ancestor is also enqueued
			var parVm = vm;
			while (parVm = parVm.parent()) {
				if (redrawQueue.has(parVm))
					return;
			}

			vm.redraw(true);
		});

		didRedraws(redrawQueue);
		redrawQueue.clear();
		rafId = 0;
	}

The forEach function is documented to not observe any elements added while the loop is iterating, so any redraws that would get queued while processing redraw hooks are immediately discarded by the redrawQueue.clear().

Instead the function should extract the array, clear the queue, and then requeue the drain call if the queue has new entries. Something like this (untested, off the cuff):

	function drainQueue() {
                let elms = redrawQueue.entries();
		redrawQueue.clear();
		elms.forEach([vm,_] => {
			// don't redraw if vm was unmounted
			if (vm.node == null)
				return;

			// don't redraw if an ancestor is also enqueued
			var parVm = vm;
			while (parVm = parVm.parent()) {
				if (redrawQueue.has(parVm))
					return;
			}

			vm.redraw(true);
		});

		didRedraws(elms);
		rafId = 0;
                if (redrawQueue.size() > 0) 
                    requestAnimationFrame(drainQueue);
	}

I can take care of this if you wish. May want to consider a counter to limit the number of cycles as a failsafe; after, say, 1000 redraws interrupt the cycle with a log message indicating so.

@lawrence-dol lawrence-dol changed the title Redraws called from a DidRecycle lifecycle are ignored. Redraws called from a didRecycle hook are ignored. Mar 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants