Skip to content
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

Create ISelector<TState, TValue> class #221

Closed
mrpmorris opened this issue Oct 29, 2021 · 18 comments
Closed

Create ISelector<TState, TValue> class #221

mrpmorris opened this issue Oct 29, 2021 · 18 comments
Milestone

Comments

@mrpmorris
Copy link
Owner


Current person is @CurrentPerson.Value?.Name

@code 
{
  [Inject]
  private ISelector<MyState, Person> CurrentPerson;

  protected override OnInitialized()
  {
    CurrentPerson.Select(x => x.People[x.CurrentPersonIndex]);
  }
}
@szalapski
Copy link

Is this a idea after you wrote "There is currently no way to react to the change of subsections. I don't think it would be difficult to write something."

If so, I'm not sure how the above would help to rerender only on a person change rather than any change to People. Could you say a little more?

@mrpmorris
Copy link
Owner Author

The code in the example would identify a subsection of your state. When that value changes the component will have StateHasChanged called automatically.

So instead of requesting a whole feature state via IState, you are expressing an interest only in part of it.

@szalapski
Copy link

szalapski commented Oct 30, 2021

So in this example, the component is consuming "part" of the state in two places: in the "Current person is" text, and in the improper unassigned expression in OnInitialized? I don't quite get how CurrentPerson could both have a .Value property in line 1 and be an IEnumerable in line 10?

@mrpmorris
Copy link
Owner Author

Line 10 is State, line 1 is an object holding the result of the selection.

@mrpmorris
Copy link
Owner Author

BTW: If in your component you have a member that holds the previous value, you can override ShouldRender in your component to return false if it hasn't changed.

@szalapski
Copy link

szalapski commented Oct 31, 2021

OK, you are using the method name "Select" for the function that defines which portion of the State I want to depend on, then thereafter CurrentPerson.Value contains that portion of the State?

So that means before I call Select, CurrentPerson.Value is null, or throws an exception, or something like that?

BTW: If in your component you have a member that holds the previous value, you can override ShouldRender in your component to return false if it hasn't changed.

My package does exactly that in a way that maintains the previous value automatically, but it requires the consumer to implement a function that serves as a hash for the display state. I'm hoping that once I introduce real state management, I can mostly depend on whatever we come up with here instead.

@mrpmorris mrpmorris added this to the 5.0 milestone Nov 3, 2021
@szalapski
Copy link

Do I understand it right? You want to use the method name "Select" for the function that defines which portion of the State I want to depend on, then thereafter CurrentPerson.Value contains that portion of the State?

So that means before I call Select, CurrentPerson.Value is null, or throws an exception, or something like that?

@mrpmorris
Copy link
Owner Author

Correct, it would throw an exception

@szalapski
Copy link

Okay, I'll try this out of it gets implemented. Thanks.

@mrpmorris
Copy link
Owner Author

@szalapski Could you try out the source in https://github.com/mrpmorris/Fluxor/tree/221/PM-StateSelector

  1. Inject IStateSelection<MyState, string> NameState;
  2. In OnInitialized call NameState.Select(x => x?.Name);

Then it should only trigger a component render when the name changes

@szalapski
Copy link

szalapski commented Nov 10, 2021 via email

@mrpmorris
Copy link
Owner Author

No, you'd have to add source to your project

@szalapski
Copy link

OK, I'll try that now.

@szalapski
Copy link

szalapski commented Nov 12, 2021

It appears it won't build without MrPMorris.snk. I tried manually disabling strong naming for both projects, but I still get:

3>------ Rebuild All started: Project: Fluxor.Blazor.Web, Configuration: Debug Any CPU ------
3>CSC : error CS7027: Error signing output with public key from file 'MrPMorris.snk' -- File not found.
3>Done building project "Fluxor.Blazor.Web.csproj" -- FAILED.

Would it be easier for you to provide a pre-release nuget package?

EDIT: I created my own .snk file to get past this and am trying it out.

@szalapski
Copy link

szalapski commented Nov 12, 2021

Yes, this works. When only part of the state changes, the components that depend only on the unchanged properties are not rerendered.

Now I am wary of all the ceremony code this approach requires. I see now how the dynamic nature of Javascript makes such maneuvers easier there, such as with Vuex or Redux. I can't help but think there should be a way to do this without configuring it in every component; rather, to deduce dependencies simply from the getters a component uses.

I think it would take aspects (e.g. using PostSharp): Could it be possible to create an aspect that runs after every state property's getter runs which would register which component has a dependency, and create a corresponding aspect for every setter in the the state object that will read those registrations and trigger a StateHasChanged in the components that are registered for the corresponding getter?

@mrpmorris
Copy link
Owner Author

How would you implement it in VueX / Redux?

@szalapski
Copy link

szalapski commented Nov 12, 2021

The nature of Vue means that properties on components automatically get "rewritten" to have getters and setters internally, so that the internal black-box setter of a property triggers code that makes every setter go out and reactively trigger each getter. VueX uses these properties naturally so that any mutation cascades through to all the right places and causes no processing in any components that don't use it.

In C# and Blazor, we don't have reactivity; instead, we have things that trigger a re-render. So the equivalent would be to make a reducer--or, more likely, other code in Fluxor that runs the reducer--to compare old vs new, figure out the properties that are changed, figure out the components that consume those properties, and call StateHasChanged only on the components that care about the changed property. Seems difficult.

@mrpmorris
Copy link
Owner Author

In angular rxjs the values are subscribed to when you pipe them. A change to that value causes a change only to the parts of the UI that are subscribed.

Blazor does it differently. It generates a tree in memory, diffs it with the last tree rendered, and then only renders the differences in the DOM.

There is no way to do that only for parts of a single component. The most finely grained you can get is component level. So you'd have to break your single component into many and nest them.

Writing a deep compare for Fluxor isn't an option.

If you created a record type for your view, you could Select() transform your State into one of those and then Fluxor would use the deep compare built into .NET.

It would possibly be faster than comparing a render tree when you don't need to render, but when it does need to render then Blazor will still have to diff trees anyway. So if it will save you any CPU or not, I don't know.

mrpmorris added a commit that referenced this issue Jan 13, 2022
* Remove need for index.js #235 (#236)

* Remove need for index.js (#235)

* Update to V5

* Separate IDispatcher / IStore #209 (#237)

* IStateSelector<TState, TValue> #221 (#238)

* Language version 9 features

* Added 5.0 breaking changes to releases

* Fix JS accidentally introduced error in ReduxDevTools middleware

* Make generic action types human readable in ReduxDevTools #205 (#249)

* Make generic action types in ReduxDevTools human readable #205

* Move actions to common folder in tutorials #250 (#251)

* Update versions to 5.0.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants