Closed ArtemyB closed 2 years ago
Lazy views can probably help with this.
I believe similar behavior occurs even in a plain React function-component application. I think "render" in this context only means "execute the function" and doesn't necessarily indicate a visual browser render. There will still be React's DOM reconciliation process happening afterward.
Try the following in a plain React app and note the Child component without any changes still gets highlighted by the dev tools upon Parent component update.
import React, { useState } from 'react';
function Child() {
return <p>Child component</p>;
}
function Parent() {
const [count, setCount] = useState(0);
return (
<div>
<p>Parent component</p>
<button onClick={() => setCount(count + 1)}>Clicked {count} times</button>
<Child/>
</div>
);
}
export default Parent;
If you make the Child a memo then the dev tools will no longer highlight it:
const Child = React.memo(
function Child() {
return <p>Child component</p>;
}
);
Using lazy view in Elmish should be analogous to this.
Hello @ArtemyB ,
sorry for taking this long before looking at this issue, I was focusing on releasing Nacara.
React integration of Elmish, has been intensively tested in the past and should be correct in theory. At least when using Fable.React bindings.
As mentioned by @zanaptak, when I take a pure JavaScript project and look at the flash all the components flash too.
https://user-images.githubusercontent.com/4760796/140612933-ab571ac7-95f8-4bd3-9567-d2e6f37476da.mp4
Example from https://github.com/arnab-datta/counter-app
During a Fable Conf, there has been a great talk about how to measure performance in a React application with Fable. https://www.youtube.com/watch?v=9VJoaNoutm4
In the talk, he uses the profiler to see which components was consider for the render and which has been actually rendered.
In your application, it right that everything seems to be re-rendered:
The problem is that we can't really isolate if this is due to Feliz, Mui, or Elmish as everything is mixed together in the sample.
It would be better, if we had a minimal example in F# and JS to have a fair comparaison.
Like that there are some things to know about Elmish and React:
dispatch
and functions in general are not stable by default meaning that if you pass a function as a props to a component, React will consider that the props changes and so re-render the components.To counter that, you can make dispatch
stable by using React.useCallback
I believe and you can also use functional components like React.functionComponent
, React.meno
(from Feliz) or FunctionComponent.Of
, Memo.Of
(from Fable.React) with a custom comparer like memoEqualsButFunctions
or equalsButFunctions
which ignores the functions when doing the comparaison.
Generating a Guid
in the component means that this components will always be different between rendering because you are generating a new Guid
at each render. Example
About LazyViews
they are components offered by Elmish which can be used to optimized the React diff. I think, they are not used that much now days, because it was before we had Hooks
and real function component from React.
Actually, I think dispatch
has been made stable in Elmish 3
https://github.com/elmish/elmish/issues/157#issuecomment-439927352
But this is true only for the top level dispatch
as when you due MyMsg >> dispatch
you are creating a new function on each call.
Using the lazyView
components allow to have finer control over what is re-rendered by React:
But I am unsure how React does its diffing or if the extension works correctly. Because, even for simple application I have some strange behaviour.
Hello @vbfox , I am pinging you in case you are still doing some Fable / React stuff as I know you invested a lot of time in it in the past.
@MangelMaxime @zanaptak thank you for the detailed answers! 👍 For the link to the video from FableConf -- special thanks. However it required working with React (and JS) for some time to better understand what is actually happening in apps and to feel less confused. But all the tips from the answers above helped a lot (also they addressed questions/topics that needed to be examined in more detail). So closing this as it actually appeared to be just a question, not an issue with Elmish.React. Again, thanks a lot!
Description
While working on my Feliz + Elmish app I've discovered that the whole global state dependent part of the app tends to re-render on every app state change (according to React Dev Tools with "Highlight updates when components render" setting being enabled), even though I pass only necessary parts of the global state to child views. For example one of the pages has a form, the state of which is a part of the global Elmish state. Even one form's input change causes the whole app UI to re-render. Here is a link to my demo-app: https://github.com/ArtemyB/ElmishDemo
Probably this is not an issue of the library, but my incorrect use of it. If so I would be glad to know any remarks on that.
Repro code
https://github.com/ArtemyB/ElmishDemo
Expected and actual results
Expected
To see re-rendering only the changing UI component. In case of the Login form from the provided example it should be only
LoginFormView
at least.Actual
To observe the issue I am describing "React Developer Tools" browser extension should be installed and its "Highlight updates when components render" setting should be enabled. And then it is enough just to type something into any of the Login form fields to see that on any change all the app UI components are highlighted by "React Developer Tools".
Related information