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

Save server-side actions #68

Closed
low-ghost opened this issue Aug 14, 2015 · 14 comments
Closed

Save server-side actions #68

low-ghost opened this issue Aug 14, 2015 · 14 comments

Comments

@low-ghost
Copy link

Hi gaearon and community, thanks for the fantastic tools!

I was wondering if there was any way to save/serialize redux actions that had been executed on the server so that they would appear in DevTools. At the moment, I have very similar code on server and client with both allowing a dispatch of a promise based action that updates the store in a standard redux way. Obviously, client side works flawlessly thanks to this wonderful code base, but at the moment, the server side actions don't appear even though they effect the initial state. It'd be neat to be able to see that these were the results of a GET_DATA server action, for instance, even if time-travel might or might not be possible. Any way this is already possible? Any thoughts on how I might lend a hand if not/ if it isn't fundamentally impossible?

@gaearon
Copy link
Contributor

gaearon commented Aug 14, 2015

They should appear. Make sure devTools() appears after applyMiddleware() in your compose() call. Related: reduxjs/redux-thunk#9.

@low-ghost
Copy link
Author

Whoa, awesome! Not quite the mistake I made, for some reason I had middleware = applyMiddleware(promiseMiddleware) then middleware(compose(...)). Anyhow, already working. Thanks a million!

@low-ghost
Copy link
Author

hmm well, guess I got a little excited there. Forgot I still had client actions running. I'm sure I'll figure it out with a bit of tinkering, but if you happen to have a second, my set up now looks like

if (DEVELOPMENT && CLIENT && DEVTOOLS) {

    finalCreateStore = compose(
      applyMiddleware(promiseMiddleware),
      devTools(),
      persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/)),
      createStore
    );

} else {
  finalCreateStore = compose(
    applyMiddleware(promiseMiddleware),
    createStore
  );
}

and I have <DebugPanel /> etc only on client side. Anything else I could be overlooking?

@gaearon
Copy link
Contributor

gaearon commented Aug 14, 2015

This looks good to me. Please post an example that reproduces the problem and I can take a look.

@low-ghost
Copy link
Author

that'd be great! here's a link to a repo that I've minified as much as possible before it became confusing or not actually run-able in case it's more of a server/setup issue. It's heavily based on this tutorial: https://medium.com/@bananaoomarang/handcrafting-an-isomorphic-redux-application-with-love-40ada4468af4 with a good bit of ideas from https://github.com/erikras/react-redux-universal-hot-example.

The only real innovation is that instead of collecting promises from containers and executing them in the context of the router as in the redux-universal example, they execute as standard redux dispatches sending out PENDING, <THE_PROMISE_TO_EXECUTE>, and REMOVE_PENDING actions via a slightly modified redux promise. The store has an observer (not Rx) that prevents react rendering until the PENDING count hits 0.

@gaearon gaearon reopened this Aug 15, 2015
@gaearon
Copy link
Contributor

gaearon commented Aug 15, 2015

Can you help me by explaining which precisely actions you expected to see? Here's my output:

(you should update to 1.0 too :-)

screen shot 2015-08-15 at 17 37 32

@low-ghost
Copy link
Author

Sure, I'd expect to see a sequence of PENDING, GET_TODO_LIST, and REMOVE_PENDING for the server and an additional one for the client. The ones that are showing are client based, the GET_TODO_LIST having a payload with text 'client async task 1' and 2. They are set off from the same function in the home container. At least in my set up, I get an initial state showing two tasks with 'server async task' but no PENDING... sequence showing how they arrived as state

@low-ghost
Copy link
Author

And yep, 1.0 is gorgeous!

@gaearon
Copy link
Contributor

gaearon commented Aug 15, 2015

Maybe I just don't understand what you mean by "client" and "server" actions. Can you dumb down the example for me, so I can clearly see what you mean?

@low-ghost
Copy link
Author

So the general flow is that a node based server finds all react components with a prepareRoute static method before it calls React.renderToString. These methods are executed and dispatch promise based actions. The middleware sends a PENDING action to make redux aware of an unresolved promise and will send a REMOVE_PENDING on resolution. The node server creates a subscribe to watch 'pending' and only moves on to renderToString when all pending promises are accounted for (the count is 0). All this on the server redux which will have a record of actions PENDING, GET_TODO_LIST, and REMOVE_PENDING.

After the html response is sent and react attaches listeners etc, the app performs the same sequence. The only difference being that on the server getTodoList performs a require() on a file in api while the client getTodoList performs a fetch(). What I'm wondering is if the PENDING etc generated before renderToString and before react boots on the client could be serialized and represented in DevTools.

@low-ghost
Copy link
Author

This might actually be a larger scope than I was thinking. Forgive me if I have no idea what I'm talking about, but I think this is a situation of synchronizing two distinct redux instances. I'm thinking this would entail exporting a report of actions and state updates from one and importing them into the other. As long as the shape of data was similar, this could look like react's diffing of the components from the server. Redux could scan a checksum of an initial state object and update accordingly

@gaearon
Copy link
Contributor

gaearon commented Aug 15, 2015

This makes perfect sense and I see what you mean now. I think it's not hard to implement because basic pieces are all there, but right now it won't work. Let's keep this open and revisit after a while.

Related: reduxjs/redux#350 (not obvious why, but it is ;-)

@low-ghost
Copy link
Author

Awesome. Thanks for taking a look. I'll check in on 350 and see if I can't help out a bit

@gaearon
Copy link
Contributor

gaearon commented Sep 27, 2015

I'm closing because I currently don't have time to implement this. Now it should be easier to try hacking on it, since we have tests in the project, so feel free to explore them and give it a go. Basically the problem is that there's no API to hydrate “lifted state” (see the tests and the source to get an idea of what it is) into the store.

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