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

Project status? #1

Open
markerikson opened this issue Sep 3, 2018 · 8 comments
Open

Project status? #1

markerikson opened this issue Sep 3, 2018 · 8 comments

Comments

@markerikson
Copy link

Hiya. I've been busy focusing on React-Redux v6, but I ran across this repo and was curious what the status is. I see a long design doc, but no other commits in the last few months.

FWIW, some of the concepts of "automatic state access" tracking are very much in line with things that I would like to see built into React-Redux, particularly because that would let us optimize which connected components actually get updated via context when an action is dispatched (per my discussion in reactjs/rfcs#60 ).

@theKashey
Copy link
Collaborator

Hey @markerikson. Project is not dead. I am slowly moving towards the goal, but there is some problems I have to (properly) solve first. Like

  • memoization. Reselect is killing all proxy-based operations, as long it does not repeat property access on second run. After second run system will think that something does need data, and making the next step will just freeze component forever.
  • tree based subscriptions, and how to use changed bits for it. We knew that any connect method needs, but how to run only it in the New React? May be split "story" into the different contexts, not have more that 32 bits to work with?
  • another approach is to have changes paths as a part of a store, and trigger all the connected components.

The current approach is not provide special factory to make any memoization function be compatible with proxy. Like const createSelector = adopt(realCreateSelector), nothing more. Then in case of memoization adopt will emulate access to the same points as before, to keep access model consistent.

@markerikson
Copy link
Author

markerikson commented Sep 4, 2018

@theKashey : see the linked React RFC thread for my rough idea of how React-Redux could use changedBits. Really short version:

  • <Provider> is the only subscriber, and we assume a plain object top state value
  • Whenever the subscriber callback runs, it checks to see which slices of state have changed. It hashes the key names into bits, using some algorithm that consistently turns a given key name into the same bit every time.
  • Connected components somehow indicate which state slice names they're interested in, and we do the same key-> bit hashing there
  • As a result, most connected components could completely ignore state updates that don't concern them.

It's okay if some key names happen to hash to the same bit value - the general idea is just to cut down on the impact somewhat.

I'm sure more sophisticated approaches are possible, this would just be a relatively basic approach.

@theKashey
Copy link
Collaborator

Let me rephase:

  • is the only subscriber, and we assume a plain object top state value.
  • We are looking for a good hashing functions. For last 60 years.
  • connected elements somehow knows what that need. Somehow.

Also - sometimes you should dive deeper into the store, making slices smaller. But even top level separation will help a lot.
But first one have to solve memoization problem.

@markerikson
Copy link
Author

I'm not quite sure I follow what you were saying in that comment.

For the "hashing function" side, I briefly experimented a couple months ago with using a "minimal perfect hashing" algorithm, which seemed to produce reasonable results - see here: https://codesandbox.io/s/o5vpjpq5lq .

Ideally, I'd like to have connected components auto-calculate what keys they're accessing, per the ideas you've discussed in this repo. If we can figure out a good way to do that, great! If not, we can still potentially add an option to connect() to let the end user specify what top-level state keys that component is interested in. Or maybe something else - it's still just ideas at this point.

@theKashey
Copy link
Collaborator

theKashey commented Sep 5, 2018

So, let me explain what I've got today:

Proxyequal - as a "base layer". Wraps state and records all the operations on it. After being used it will produce a list and a trie with all values used, and how exactly they were used. Ie then you are doing state.a.b - you need only b, but could early-deside(memoize in this case) if a still the same.

This library is all you need to make a first step. But not the second step.

The problems are

  • selectors are not constant. In the different conditions, they may access different keys - state.items[props.key] - so their work should be reestimated every run.
  • there is no "every run" with memoization. On the second run memoized selector will do nothing.
  • this situation is easy to detect, proxyequal ships shouldBePure helper, which could test, that some function is doing the same every run.
  • but memoization and memoization cascades is something good to have.

So the plan is:

  • create a function, which could wrap any other memoization library, and "bubble" "access trie" to the top, in case of memoization
  • amend connect to throw in debug mode, if mapStateToProps is not absolutely pure
  • collect all "access tries" on the top level, and do the job.
  • but it is still a bit fragile.
  • probably call all mapStateToProps in dev mode (or in tests), and throw throw errors, if something got unpredicted updated, ie was not "reading" some keys before.

Another option:

  • add another option to connect, with information about "what it will do, which parts of the state it gonna access". In case of breach - console.log settings to be updated.

Ie be more declarative and let js help you write down these declarations.

All this stuff is hard to write, but easy to test. But this behavior doesn't sound like something production ready.

@markerikson
Copy link
Author

Yeah, Leland Richardson put together a function similar to shouldBePure a while back ( https://gist.github.com/lelandrichardson/ff2392199b62c26759f2bf235676758b ), and someone else built on that idea ( https://github.com/mmahalwy/redux-pure-connect ).

I'd say that if we wanted to do some initial heuristics, a component whose mapState is just (state) => {} would be safer to check, while one that is (state, ownProps) => {} would be less so. Perhaps it could also be opt-in behavior, where the end user turns on some additional option to connect.

@theKashey
Copy link
Collaborator

In your example ensureShallowPurity checks that mapStateToProps, being called twice with the same parameters, results the same object.

Better give a shot to
https://github.com/theKashey/why-did-you-update-redux, which uses proxyequal underneath, and could detect any memoization issues.

shouldBePure does not check how pure is external behaviour of a function. Ie how pure it is, and any memoized function would be pure.
It checks internal behaviour. Given the same arguments function should not only return the same object, but perform the same operations to do it. And any memoized function is not pure in this context.

shouldBePure detects that proxy-based field access tracking could be used. Ie you can rely on it.
PS: And I did mistake - it is not a part of proxyequal, but of memoize-state
See https://github.com/theKashey/memoize-state/blob/master/src/utils.js#L34

@theKashey
Copy link
Collaborator

A quick fix would be to put a Proxy in front of server-side Redux store and only serialize what actually got used to generate markup.

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