Closed scottsauber closed 5 years ago
I just watched this talk regarding hooks and I must say there are some awesome ideas in there. Custom hooks especially. Extracting the state and the side-effects/lifecycle-methods into one custom function for reuse looks amazing. https://youtu.be/dpw9EHDh2bM
I personally would love to get rid of the lifecycle-methods If we had a better way.
For state I currently use Blazor.State
, which uses MediatR
to handle state-changes. I find that approach really elegant and I am already used to MediatR.
I am also not a big fan of <CascadingValue>
. It feels clunky and makes the code less transparent IMHO.
Maybe there are better ways. I believe now is the right time to think about those things, before we can't change it back. Just my 2 cents.
Okay...that inspired me to dig into the ideas a bit more.
I think the big deal with hooks for React folks is that it removes a lot of the boilerplate and ceremony of React components. The need for this stems from how they designed React to begin with and also has to do with the fact it's Javascript.
A lot of what hooks solve we don't really have to deal with in Blazor...it's more like React is moving the developer experience closer to how the Blazor experience is (easy set/get of local state, dependency injection, less ceremony). That being said, there are some interesting ideas in Hooks that don't exist in Blazor as well as some aspects of Blazor that are a bit "messy" to work with in Blazor's current state.
I created a project to explore those ideas and aspects. It takes the React Hooks example from the video and translates it to Blazor and then iterates on it to clean it up and to play with some of ideas from Hooks. A few main things became apparent:
Javascript/DOM interop is messy in Blazor (and WebAssembly in general). Having wrappers around it really really helps clean that up.
DI instead of cascading values FTW.
Dealing with events from sources outside the component can be tricky.
I've run up against this in other Blazor projects I've been playing with. To help, I created a simple hook-like mechanism for subscribing to (and automatically unsubscribing!!) from external events.
Created a simple hook-like mechanism for running code after each render.
It could be tweaked to work essentially the same as the useEffect()
hook (although it doesn't right now) where you could specify one-time execution, after-each-render execution, as wel as logic for cleaning up once the component is removed. I think (could be wrong) that this removes most of the need for lifecycle methods.
If you're interested, here's the project: https://github.com/andoband/BlazorHooks.
And here's what the Hook example can look like in Blazor today. Note, this is essentially Demo 5 from my project except I didn't implement Section and Row components in the project.
@inherits SubscribeActions
@inject ThemeInfo theme
@inject LocaleInfo locale
@inject DomWindow window
<Section class="@theme.SectionClass">
<Row label="Name">
<input bind-value-oninput=@name />
</Row>
<Row label="Surname">
<input bind-value-oninput=@name />
</Row>
<Row label="Language">
@locale.LocaleName
</Row>
<Row label="Width">
@window.Width
</Row>
</div>
@functions
{
string name;
string surname;
protected override void OnInit()
{
// Both of these are similar (in different ways) to using useEffect() hook in React
Subscribe(() => window.OnWindowResized += StateHasChanged,
() => window.OnWindowResized -= StateHasChanged);
AfterRender(() => window.SetTitle($"{name} {surname}"));
}
}
There's nothing ground breaking there, but it does provide me with a few patterns that I'm going to use from now on.
@andoband Thank you, that looks really really nice. Demo5 is the way to go!
Thanks for contacting us, @scottsauber. We have no plans to do this in the near future.
Just wanted to open a discussion about the new React Hooks which was announced at ReactConf. I reach for React when I do a SPA currently (although we have a non-critical IT only app we built in Blazor as well), and React Hooks have me pretty excited, because it groups similar logic together much like Feature Folders do for a folder structure.
Wrapper Hell
One of the things they discussed that React Hooks is solving is "Wrapper Hell" where you have tons of components nested beneath each other. Specifically they call out Context, which is very close to how Cascading parameters work in Blazor with the
<CascadingValue>
wrapper component.Curious what the Blazor team thinks about this and if there are any thoughts around perhaps doing something similar to what the React team is moving to with Hooks and getting rid of the need for the wrapper component.
My quick thoughts would be something like making global state injectable instead of using a wrapped component... or having access to some grab bag off of the BlazorComponent base class where you could do something like
.Get<T>
and have it magically grab your value for you. Those are my crappy 1 minute ideas, but feels like Blazor could do something similar to remove the need for the wrapper component and keep the component trees less nested and easier to follow.React is getting rid of lifecycle methods
React is also moving away from lifecycle methods like componentDidMount (OnInit/OnInitAsync in Blazor). They're mostly doing this so that setup (addEventListener) and cleanup (removeEventListener) is wired up in the same spot instead of separate lifecycle methods which led to bugs when you forgot to cleanup for instance. Curious to hear other's thoughts on this and how it would apply to Blazor. I personally haven't done any cleanup of my Blazor components, but maybe I'm causing a memory leak I don't know about.
Obviously it's very early days in React Hooks land, but the concepts behind them makes a lot of sense. Just wanted to generate some discussion. Thanks!