HenrikJoreteg / redux-bundler

Compose a Redux store out of smaller bundles of functionality.
https://reduxbundler.com
583 stars 46 forks source link

composing createAsyncResourceBundle #14

Closed greggb closed 6 years ago

greggb commented 6 years ago

Having trouble wrapping my mind around reacting and createAsyncResourceBundle.

I've created a base-data async bundle similar to the example app. I grab the data field off of this in a regular bundle. This works great, but would mean I create a separate async bundle for each reducer bundle.

Initially I used createAsyncResourceBundle and added some selectors to it, but seems like that is not the correct pattern as I was getting 'can't find selector' errors.

I have 3 main endpoints to hit at different times within the app where any one of them could be called based on URL. Perhaps my best course is to add a reactor to each regular bundle and trigger the api calls that way, although I'd lose the builtins of createAsyncResourceBundle.

Along those lines in the docs it says this about reactX:

They get run automatically by redux-bundler whatever they return gets dispatched.

Does that mean whenever that action gets called the reaction runs or if any of the input selectors are affected by a dispatch?

HenrikJoreteg commented 6 years ago

I've created a base-data async bundle similar to the example app. I grab the data field off of this in a regular bundle. This works great, but would mean I create a separate async bundle for each reducer bundle. Initially I used createAsyncResourceBundle and added some selectors to it, but seems like that is not the correct pattern as I was getting 'can't find selector' errors.

Yeah, i would just attach your selectors and a reactor to the generated bundle instead of creating a second one to go with each generated one. But, you have to make sure you're referencing the right names. If you're getting the "can't find selector" errors that really is what's going on.

I have 3 main endpoints to hit at different times within the app where any one of them could be called based on URL. Perhaps my best course is to add a reactor to each regular bundle and trigger the api calls that way, although I'd lose the builtins of createAsyncResourceBundle.

You can incorporate the URL-based condition into your reactor as follows. Each bundle created with createAsyncResourceBundle includes a selectShouldXUpdate selector that encompasses all the logic for whether or not the data should be fetched based on its age, staleness, etc. You can use that to create a reactor for the generated bundle that specifies other conditions too, such as the URL.

import { createAsyncResourceBundle, createSelector } from "redux-bundler";
import ms from "milliseconds";

const bundle = createAsyncResourceBundle({
  name: "honeyBadger",
  getPromise: () => ...,
  retryAfter: ms.seconds(5),
  staleAfter: ms.minutes(2),
  expireAfter: ms.minutes(2.5)
});

bundle.reactHoneyBadgerShouldFetch = createSelector(
  "selectHoneyBadgerShouldUpdate",
  "selectPathname",
  (shouldUpdate, pathname) => {
    if (shouldUpdate && pathname === '/your-url') {
      return { actionCreator: "doFetchHoneyBadger" };
    }
  }
);

export default bundle;

In this way you never have to trigger anything from your components. Data will just start fetching on that URL, but only if the data is stale or missing, etc.

Does that mean whenever that action gets called the reaction runs or if any of the input selectors are affected by a dispatch?

Redux bundler creates a single separate subscription using store.subscribe() that will monitor your reactors and yes, these will be evaluated on every dispatch just like any other selectors that are connected to components. It starts evaluating these until it finds one that returns a non-falsy result, this will be scheduled to be dispatched using requestIdleCallback. In this way, yeah, you never have to trigger anything manually. The current state is what triggers other actions. Even if the "state" causing it is just, the data grew stale, or the fact that you're on a certain URL.

Hope this all makes sense... if something isn't clear enough in the docs, there's a docs folder that could probably use some more love :)

greggb commented 6 years ago

Thanks for taking the time to write that up. What you described was my initial approach, but I must have a subtle bug. I'll mess around with it some more and see if I can clean up the docs with findings.

greggb commented 6 years ago

Ah, here's where I was going wrong - I can't add the selectors/reactor inside the bundle creator. Each needs to be added later bundle.selectSomeSubState, or export default {...asyncBundle, additionalSelectors}

HenrikJoreteg commented 6 years ago

@greggb yeah, that'd do it. I'm going to close this, then. Feel free to re-open if you're still having issues.

greggb commented 6 years ago

np, I was looking at it the wrong way - thought it would merge anything I pass in with the async bundle, but it's really just a helper for a standalone bundle.

I see the use case for this now.