GithubHelp home page GithubHelp logo

Comments (19)

dai-shi avatar dai-shi commented on May 29, 2024 1

My current implementation of useReduxStateMapped has several issues: (1) there is no performance benefit. (2) it doesn't yet work without external memoization. (3) it can't deal well with multiple selectors/conditional usage.

All right, let me start over. Please be patient. The good news is now that I have better understanding of your requirement.

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

I'll push the implementation later. Meanwhile, here's the benchmark result.

react-redux 7.0.0-beta.0

Results for benchmark stockticker:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Avg FPS β”‚ Render       β”‚ FPS Values                                                                          β”‚
β”‚         β”‚ (Mount, Avg) β”‚                                                                                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 28.46   β”‚ 415.6, 0.6   β”‚ 27,30,25,31,23,38,33,21,27,26,32,35,25,32,27,30,25,30,25,33,31,27,29,25,32,24,20,20 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

useReduxStateMapped

Running benchmark stockticker-rher
    Checking max FPS... (30 seconds)

Results for benchmark stockticker-rher:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Avg FPS β”‚ Render       β”‚ FPS Values                                                                          β”‚
β”‚         β”‚ (Mount, Avg) β”‚                                                                                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 22.77   β”‚ 380.0, 0.5   β”‚ 21,19,28,22,26,21,25,28,23,25,19,26,21,26,19,26,27,18,22,16,22,21,19,24,20,23,26,26 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

I guess closures are not an issue in a proxy-based world, re: @epeli

regarding the FPS, they used to be 45 and 51 FPS for the react-redux 7 benchmark. I guess your computer is running slower. So that said, your version seems to be closing the gap, and solving the stockticker problem, nicely done!

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

One thing I would look at is this:

  • how many times is Pair rendering when ${c.UPDATE_PAIR}_${sliceId} is dispatched?

How many times is the corresponding render function called in the react-redux 7 benchmark?

It's possible that the state change causes both Slice + Pair to render, and only one is necessary. I might not know the code well enough. But my intuition points to this being the difference. RHER currently isn't smart enough to cancel re-rendering the child, but somehow react-redux is in how it handles top-down rendering. In other words, if you know a subscription is from a child, don't notify it if the parent is already rendering.

Maybe this doesn't apply. I'm just giving what insight I know from studying react-redux in the past...I actually I see, Slice has no reason to update when a single Pair's value changes. If that's not the rendering characteristics, perhaps something needs to change within proxyequal itself. Perhaps more than one Pair is rendering, etc. My hunch is the whole Slice is rendering when it shouldnt. That seems to match the difference in FPS.

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

You can make it faster by not doing closures. Because then you can memoize the arguments like in react-redux + reselect.

So here's what you want it to look like:

const mapState =(state, current, sliceId, pairId) => {
    const { value, name } = state[sliceId][pairId];
    const direction = value > current ? "up" : "down";
    return { value, name, direction };
  };

const Pair = props => {
  const prevValue = useRef(null);
  const deps = [prevValue.current, props.sliceId, props.pairId]
  const { value, name, direction } = useReduxStateMapped(mapState, deps);

Notice 2 things:

  • we are being told the arguments/deps. therefore we can compare their values to the previous args
  • mapState passed by reference, so we can use it as a key to make comparisons to previous values
import memoize from 'memoize-state' // https://github.com/theKashey/memoize-state

const selectors = {}
const cache = {}

const useReduxStateMapped = (mapState, deps) => {
   // work related to proxyequal access tracking

   // work if react-hooks-easy-redux determines changed state is being used:

   const memoizedSelector = selectors[mapState] = selectors[mapState] || memoize(mapState)
   const result = memoizedSelector(store.getState(), ...deps)

   if (stateChanged) {
      if (cache[memoizedSelector] === result) return bailout()
      return cache[memoizedSelector] = result
   }
   else if (propsChanged) {
        return cache[memoizedSelector] = result
   }
}

if @theKashey exposes an API to check whether the result value was in the cache of memoizedSelector, we don't need to maintain a duplicate in cache. ...eg:

const { result, isCached } = memoizedSelector(store.getState(), ...deps)

This hierarchical bail-out mechanism should get your the benchmarks that rival react-redux 7.

NOTES:

  • you can't bail-out if props change (because it's expected behavior for the component to update in response to those properties anyway), but if state changes, you can bail out (as the notification was received from the store subscriptions, not a parent component).
  • we can't use useMemo because it doesn't work outside of render (i.e. as triggered by subscriptions), and even if it could, it can't be conditionally used.
  • there's a userland weakness: if you would like to use the selector in multiple places, you have to use a factory to generate one for each component that will use it, which is one of the annoyances of reselect and why re-reselect exists, which also is not automatic/transparent enough for what we are aiming for.

@theKashey's other library might be able to solve "the reselect issue" because of its unique way to cache multiple values without potential for a memory leak (using WeakMaps): https://github.com/theKashey/kashe

@theKashey is the caching expert; im sure he knows exactly what to do here.

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

Ultimately, useMemo solves similar problems to re-reselect but in a more automated way. React's new rendering architecture allows hooks to be linked nicely to runtime instances of components, right. If we could use that mechanism, we no longer need references to our selector functions. Instead we can just assume we are being passed the same one as the last time the component function rendered.

So that works when props change, but not when state changes. The problem, as I mentioned, is we need to be able to locate this memoized function in response to store updates. Maybe all we need to do is keep a reference to it, which we can also call when the store updates. Perhaps useRef is the answer.

The way I would approach this problem is by implementing something similar to the code sample above, as it will work (with the aforementioned caveat). After that proves useful, then look into an improved version based on useMemo (or more than likely its internal implementation).

from reactive-react-redux.

theKashey avatar theKashey commented on May 29, 2024

We need a short and clear example to optimize.
There are two different things we are talking about:

  • some changes on code level. How we might do things
  • changes to the code structure/style. Which we could not control.

Which we could not control? What if create a new linting rule, which would do something we want, like a new react-hooks linting. But it's still a open question - what we want to do.

There is an idea - life outside of react, and be happy.
There is another idea - life inside component and be happy.

There is a need - to bailout if state change is not driving view change

There is a bug - if you will pass down to a children some "object"/"array" - you will pass a proxy-ed version of it. (there is a method, deproxify, to unwrap objects). We need injections on createElement level, or even on return level to perform this operation.
Selectors also would be a great place to deproxy.


We need to clarify what we actually need, and what is good to have.

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

bro, i dont understand you

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

There seems various subjects in this topic, but let me ask one thing at the beginning.

As I implement useReduxStateMapped #17, I see no need to proxify the result for mapState.
What do you think?
@theKashey

from reactive-react-redux.

theKashey avatar theKashey commented on May 29, 2024

You still have to properly memoize internals of mapState to pass shallowEqual comparison, while this is where common redux applications sucks.
You might try to wrap mapState with memoize-state(library idea was to "fix" mapStateToProps) to transparently (for you) memoize it as a whole.

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

so basically @dai-shi he's saying that memoize-state has proxyequal access checking built-in, so by using it you don't have to do the same work here.

But I think you still have to confirm the selector was even used. If the component function doesn't call the selector, there's no point in triggering a re-render.

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

You still have to properly memoize internals of mapState to pass shallowEqual comparison.

Oh, yeah. I totally miss that part. Let me take some time thinking about it.

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

you can't accept props in closures for memoization. You need to accept them as arguments, which you can accept in a similar api to the dependencies arrays used elsewhere by the new React apis.

useReduxStateMapped(mapState, [arg1, arg2])

We can at least do the work of passing the args to mapState. It's a bit different than the React APIs where the args are not args, but solely dependencies (and the values passed via closures). But in our case, it also reduces duplication: i.e. u dont have to pass the args twice like this:

useReduxStateMapped((state) => mapState(state, arg1, arg2), [arg1, arg2])

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

But I think you still have to confirm the selector was even used.

Yeah, I see this is necessary for Respond Framework. πŸ€”

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

It’s necessary to achieve best perf even without.

To be clear it’s a less common case, as usually U won’t conditionally not use a selector u already started to use, but it is real. Hooks themselves can’t be conditional (which is shitty), so ur effect code will run even if it u never used the selector returned by ur hook.

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

conditionally not use a selector u already started to use

Is the condition from props?

const Pair = props => {
  const { a } = useReduxStateMapped(...);
  const value = props.b ? a : props.c;
  ...
};

Something like this? Yeah, this could happen.
(My recent commit e5d3c40 should cover this. Still experimenting. It didn't, not yet.)

Any other case without props??

from reactive-react-redux.

faceyspacey avatar faceyspacey commented on May 29, 2024

Exactly

from reactive-react-redux.

dai-shi avatar dai-shi commented on May 29, 2024

https://www.npmjs.com/package/reactive-react-redux/v/2.0.0
Please give it a try. @faceyspacey

from reactive-react-redux.

theKashey avatar theKashey commented on May 29, 2024

πŸš€

from reactive-react-redux.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.