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

[universal-redux] Hydrate store "on page load" #17

Closed
0xsven opened this issue Nov 30, 2016 · 4 comments
Closed

[universal-redux] Hydrate store "on page load" #17

0xsven opened this issue Nov 30, 2016 · 4 comments
Labels

Comments

@0xsven
Copy link
Contributor

0xsven commented Nov 30, 2016

I would like to hydrate my store with user data "on page load" (meaning: before initial render). Basically something like that:

if sessionCookie {
    call api to get user data and put into store
} 

That code should be shared with the application so I can reuse it for the login form.

I am not sure when and how to do this with server side rendering. My thoughts are:

  • It feels like this should happen in a saga. But how do I run this saga before initial render?
  • Should it run on the server? I think yes
  • Alternatively I could use my own Api for login that shares the code with SSR

It would be great if you could give me your thoughts.

@diegohaz
Copy link
Owner

Hi, @0xsven

I'm currently working on a project that implements it. It's still unfinished, but you can check that out to see how this works: https://github.com/tramaLabs/web

These are the steps to implement that:

1. First you need to install some isomorphic cookie handler. I'm using react-cookie;

2. Create methods to automatically set tokens to the API requests on your API handler:

// ...
export const setToken = (token) => {
  api.defaults.headers.common['Authorization'] = `Bearer ${token}`
}

export const unsetToken = () => {
  delete api.defaults.headers.common['Authorization']
}
// ...

See src/services/api/index.js

If you do not want to do that, you'll need to add the token for each request.

3. Create a store to handle authentication and save/remove cookies on sagas:

// ...
import cookie from 'react-cookie'
import { setToken, unsetToken } from 'services/api'

// ...

export function* loginFlow () {
  while (true) {
    const { token } = yield take(AUTH_SUCCESS)
    yield [
      call(cookie.save, 'token', token, { path: '/' }),
      call(setToken, token)
    ]
    yield take(AUTH_LOGOUT)
    yield [
      call(cookie.remove, 'token', { path: '/' }),
      call(unsetToken)
    ]
  }
}
// ...

See src/store/auth and src/store/auth/sagas.js

4. Get/set the token on the server side:

// ...
import cookie from 'react-cookie'
import { setToken } from 'services/api'
// ...
cookie.setRawCookie(req.headers.cookie)
const token = cookie.load('token')
const memoryHistory = createMemoryHistory(req.url)
const store = configureStore({ auth: { token } }, memoryHistory)
const history = syncHistoryWithStore(memoryHistory, store)

token && setToken(token)
// ...

See src/server.js#L28-L34

5. Get/set the token on the client side:

// ...
import { fromAuth } from 'store'
import { setToken } from 'services/api'
// ...
const initialState = window.__INITIAL_STATE__ // has { auth: { token: 'foo' } }
const store = configureStore(initialState, browserHistory)
// ...
const token = fromAuth.getToken(store.getState()) // getToken is a selector on the auth store

token && setToken(token)

See src/client.js#L14-L22

Tell me if it helps you. I'll add this to the boilerplate soon.

@diegohaz
Copy link
Owner

Also, read this section on the README for running sagas on the server side.

@0xsven
Copy link
Contributor Author

0xsven commented Nov 30, 2016

Step 4/5 is where I was stuck! Thank you.

I guess the api call "to get user data" I would do in some componentWillMount function of the particular component that needs the data?

Btw. this thing I found quite helpful: https://github.com/mjrussell/redux-auth-wrapper

@diegohaz
Copy link
Owner

You can do that and it will work on the client, but the server will not wait for the request to complete. There're many ways to solve this problem. The one I like, which is adopted by this boilerplate, is what's done on SamplePage.

The caveat of this approach is that you can't do that on any component, but only on those are used by the router on src/routes.js (essentially pages).

@diegohaz diegohaz changed the title Hydrate store "on page load" [universal-redux|fullstack] Hydrate store "on page load" Nov 30, 2016
@diegohaz diegohaz changed the title [universal-redux|fullstack] Hydrate store "on page load" [universal-redux] Hydrate store "on page load" Nov 30, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants