dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.5k stars 10.04k forks source link

Reaction to React Hooks? #5635

Closed scottsauber closed 5 years ago

scottsauber commented 6 years ago

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!

michaelvolz commented 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.

andoband commented 5 years ago

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:

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.

michaelvolz commented 5 years ago

@andoband Thank you, that looks really really nice. Demo5 is the way to go!

mkArtakMSFT commented 5 years ago

Thanks for contacting us, @scottsauber. We have no plans to do this in the near future.