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
Timing of useEffect (error in render-test) #91
Comments
I'm trying to implement it. In fact, I haven't figured it out completely yet. Give me some time🤯. |
I tried to refactor useeffect, which now uses the requestAnimationFrame and executes before the next frame is repaint. I don't know why the test didn't pass, but the use cases worked well |
The tests aren't waiting for the next frame, so they'll execute too soon, I think? I don't think you can simply delay cleanup by another frame this way - it might appear to work fine when you're testing by just clicking around in a simple UI, but when updates trigger effects etc. in a complex app, you might for example end up trying to run a cleanup function on elements that have already been removed by another update in the previous frame... Clean up is part of the reconciliation process - it all needs to happen as one complete operation, before the scheduler can start another operation, I think? |
For deleted elements, it cannot be executed later because it is equivalent to componentWillUnmout useEffect(()=>{
setTimeout(fn,0)
}) |
But doing this in user code just creates the same problem: whatever you're doing after that timeout, you can't be sure that the state of the DOM is still what you need/expect it to be - removed elements can cause errors, and so on. It is complicated, but I'm not sure this can really be simplified. |
Halo, I spent a long time debugging our test files today. Now, only the effects test runs incorrectly. I find that this is not an implementation error of fre, nor is it because the execution time of For example, I derfer 20ms and console the effects here setTimeout(() => {
console.log(effects) // this is correct
}, 16)
expect(effects).toEqual(['effect']) Then I try to change the code: setTimeout(() => {
expect(effects).toEqual(['effect'])
}, 16) In theory it works, but it's wrong, it throws an error I'm still not familiar with jest, but for sure, it's not caused by In addition, can I close the issue about refs, except that all the tests of effects have been passed now. |
@mindplay-dk Halo, I modified the test code, and now all tests are passed. Now let me explain why.
Yes, you are right, as you say, I must defer it, so I add a const nextTick = fn => {
let start = Date.now()
while(Date.now() < start + 16){}
} Use it and wait for next frame, but how to get the results of useEffect? In your code , there is a global effects array, which can collect effect from last time: let effects = []
update 1 => []
update 2 => [effect1]
update 3 => [effect1,cleanUp1] So this is a good summary. First of all, I delayed the execution, and each update get the effect from the last time. Now our test runs well 💯 , Thank for your great job 👍 !! |
Wow! I found a better way to tests here Maybe we also can do same util |
I changed it again useEffect(()=>{
return ()=>{
console.log('cleanup')
}
},[]) cleanup will console only while the component will remove. |
You seem to be changing the test conditions with no explanation? This test makes no sense anymore. And what is this? This won't work - this will merely block the main thread for some time, then returns control to the call site. You were hoping to give time back to the renderer, presumably? You can't do it this way - calls to this function will just block for a moment and then return control to the call site without giving any time back to the render scheduler. You can't just change the test conditions whenever the tests fail - that's not how testing works. The point of testing is to satisfy a specification - it's not merely about showing how the code currently works. You can change the specification, of course, if your architecture/requirements have changed - but you shouldn't just change the test-conditions whenever they don't match what the library is doing. I can't keep up with all these changes, and I'm fairly sure that the test-suite is more or less completely broken at this point - many of the test conditions are broken, and many of the tests aren't testing the updated state of the DOM at all, so the tests have very little value at this point. I'd like to help, but you need to trust the tests (which I understand was impossible when the tests stopped working altogether because of the changes to scheduling and effects) and don't change them unless you're sure you understand what the test is for, how it works, or why your requirements changed. Ask me if you're unsure how something works, or add me as reviewer on PRs if you think the tests are wrong. Now, if you want, I can try to go over all the changes to the test-suite again, and try to restore it - but as I've said in the past, we need to figure out a way to make this library testable at the core... We need a non-blocking way for |
@mindplay-dk Specifically, I haven't found a way to test the requestanimationframe. It seems that we can make the useeffect sync (same as useLayout) and then tests it. |
I think this problem started after I pointed out the difference between Probably, what should have happened, is Right now, we don't have anything that works like React's For reference, note how |
Yeap, I've changed useeffect now, it's closer to react, but maybe we need uselayout to service the tests. |
I see that you've also been working on the test since I pulled Please see inline comments, and the comment at the bottom of the page, for various notes. As you can see, I restored basically all the test-conditions to the way they were before you started changing them - all the tests except one are now passing (if you run them individually) so as you can see, the test conditions weren't wrong, it was the framework ( There are still serious timing issues, and I solved them with hacks for now. 😣 I don't know how to fix them. Honestly, Jest is a nightmare to work with - pretty much the worst test experience I've ever had with any test-framework. 😐 Anyhow, read the comments on the commit page for more details... |
This is very useful, until this afternoon, I have now completed the code changes |
I'm not sure what you mean. Most of the test-conditions currently in |
I mean, your branch is useful~ The cause of useeffect (callback, [x]) test failure is that my code yesterday is incorrect. Please pull the latest code. Maybe it's still a bug? |
See #111 |
After using useLayout instead of useEffect, all the tests passed, so I think this problem has been solved. I'll turn it off now and turn it on later when there is a problem. |
The fix in #86 created a problem with the timing of
useEffect
.You wrote:
To work around this regression, you introduced a call to
setTimeout
, which shouldn't be necessary.An ordinary use-case for effects is in conjunction with refs - consider the following basic example, which currently doesn't work:
Note that, due to #84, I had to comment out the style assignment in the example above - this is just so you can see
ref.current
showing asundefined
on the console, which should be the element reference.So the effect is firing too soon - before the DOM updates have been applied.
To demonstrate precisely how this should work, I created an example using all 3 kinds of effects to generate console output that precisely demonstrates the order in which effects and clean-up should be dispatched:
https://codesandbox.io/s/smoosh-silence-1unrp
To demonstrate the timing of effects and clean-up in relation to the DOM updates, the result of the
test
function is printed every time - this function tests the DOM to see if the element exists and has the expected state, meaning the expected change has been applied to the DOM. In other words, this tells us whether the effect (or clean-up) function is being calledBEFORE update
orAFTER update
, and you can see this for every call on the console.Here is a comparison of output between
react
,preact
andfre
:During creation and updates, the timing of dispatch of effects and clean-up against DOM updates is currently the opposite of React. (During removal, it is correct.)
Also, the order is wrong, with new effects being dispatched before clean-up of previous effects - as well as clean-up from effects at creation being dispatched much later during removal instead of getting cleaned up during the next update as expected.
(I hope this is helpful... The approach of hands-on "tests" like this is very fragile, but I still don't know how to turn this into an automated test - which is definitely something we need to do to prevent any future regressions...)
The text was updated successfully, but these errors were encountered: