-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[Frame] hookify Loading #2303
[Frame] hookify Loading #2303
Conversation
90e8085
to
72ced88
Compare
I'm not sure how tests can improved? |
}); | ||
} | ||
const ariaValuenow = useLazyRef(() => | ||
debounce((progress: number) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this aproach component will only re-rendered when progress changes, unlike the old aproach which would re-render with each requestAnimationFrame
call. I guess it'd be safe to remove debounce and calculate the valenow inline.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a a pure function that doesn't depend on anything in the outer closure, so there's no need to put it inside the component or do any of the lazyRef stuff, just define it as a regular function outside of the component function
That said, now that we're only rendering this once per progress change I'm good with just defining this value inline. If I've understood your other changes - where progress is now a number between 0 and 1, rounded to two decimal places this should do:
aria-valuenow={progress * 100}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll refactor it and remove debounce
, just FTR if we move that function outer closure, multiple instance of this component will use same debounced ref and it might cause data leak between those instance (they mill use each other values).
</div> | ||
); | ||
} | ||
animation = requestAnimationFrame(increment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting the value of a variable outside of the current scope is surpising and we should try and avoid such mutation if possible. Instead of setting the value of an variable outside the current scope, return the value from requestAnimationFrame
animation = requestAnimationFrame(increment); | |
return requestAnimationFrame(increment); |
Then below set the value of animation
const animation = increment();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should track latest animation
for calling cancelAnimationFrame
in un-mounting, I'm not sure if const animation = increment();
will works.
should we use ref?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just chiming in const animation = increment();
won't keep track of the recursed animation as the assignment will
}); | ||
} | ||
const ariaValuenow = useLazyRef(() => | ||
debounce((progress: number) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a a pure function that doesn't depend on anything in the outer closure, so there's no need to put it inside the component or do any of the lazyRef stuff, just define it as a regular function outside of the component function
That said, now that we're only rendering this once per progress change I'm good with just defining this value inline. If I've understood your other changes - where progress is now a number between 0 and 1, rounded to two decimal places this should do:
aria-valuenow={progress * 100}
} | ||
} | ||
const increment = () => { | ||
if (currentProgress >= STUCK_THRESHOLD) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You seem to have changed the progress from being an integer between 0 an 100, to a number rounded to 2 decimal places between 0 and 1. I'm ok with that, but make sure that you update the stuck threshold and anything else like how the next step is calculated to account for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currentProgress
still go from 0 - 100 with not limited decimal point (e.g. 20, 21.332232332, 22.12321, 23.5565, etc)
nextProgress
and previousProgress
goes from 0 - 1 with only two decimal point (e.g. 0.12, 0.13, 0.14, 0.15)
with this approach set state called only when nextProgress
and previousProgress
are not equal which save redundant re-rendering. if we set currentProgress
in state, it'd re-render more often (due its decimals changes with each requestAnimationFrame
call)
componentWillUnmount() { | ||
const {animation} = this.state; | ||
useEffect(() => { | ||
let animation: number | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should animation, step currentProgress and previousProgress be refs? It seems to work ok in this closure but I'm not sure why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we don't need those outside of useEffect
I moved them all there, a race condition might occurs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should animation, step currentProgress and previousProgress be refs? It seems to work ok in this closure but I'm not sure why.
From the current implementation, it should work as-is from what I can tell. We're creating a closure, recursing and mutating the original values. setProgress
will be consistently called with the correct values, and when the component is dismounted it'll have the current animation in scope to clear.
72ced88
to
64e93ee
Compare
no worries, take your time |
componentWillUnmount() { | ||
const {animation} = this.state; | ||
useEffect(() => { | ||
let animation: number | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should animation, step currentProgress and previousProgress be refs? It seems to work ok in this closure but I'm not sure why.
From the current implementation, it should work as-is from what I can tell. We're creating a closure, recursing and mutating the original values. setProgress
will be consistently called with the correct values, and when the component is dismounted it'll have the current animation in scope to clear.
</div> | ||
); | ||
} | ||
animation = requestAnimationFrame(increment); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just chiming in const animation = increment();
won't keep track of the recursed animation as the assignment will
64e93ee
to
a889964
Compare
let me know if there's anything else than can be improved. |
Mocking can be done a bunch of ways in describe('suite', () => {
let requestAnimationFrameSpy: jest.SpyInstance;
beforeEach(() => {
requestAnimationFrameSpy = jest.spyOn(window, 'requestAnimationFrame');
requestAnimationFrameSpy.mockImplementation((cb) => {
cb();
// unique id used to cancel rafs
return 1;
});
});
afterEach(() => {
requestAnimationFrameSpy.mockRestore();
});
...
it statements
...
}) or const origRequestAnimationFrame = window.requestAnimationFrame;
describe('suite', () => {
afterEach(() => {
// This might require Object.defineProperty
window.requestAnimationFrame = origRequestAnimationFrame;
});
...
it('statement', () => {
// This might require Object.defineProperty
window.requestAnimationFrame = someMockRaf;
})
...
})
// Object.defineProperty usage
// Object.defineProperty(window, 'requestAnimationFrame', {
// configurable: true,
// writable: true,
// value: someMockFn,
// }) If you have any specific questions I'm happy to help :) |
69de5ec
to
e05f0c2
Compare
@AndrewMusgrave thanks for the tips, I get |
9c66eab
to
a06acac
Compare
a06acac
to
6e85a5f
Compare
6e85a5f
to
d8ba2fc
Compare
|
||
it('mounts', () => { | ||
expect(loading.exists()).toBe(true); | ||
for (let i = 0; i <= 100; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this a good approach?
we can also change 100
to 100,000
to have a 100% code coverage but it took ~7 seconds in my computer...
This issue has been inactive for 180 days and labeled with |
this is very disappointing, at first I thought this is a community driven project, but now I realized I was wrong. |
WHY are these changes introduced?
related to #1995
WHAT is this pull request doing?
change
Frame.Loading
to use react hooks🎩 checklist
README.md
with documentation changes