Cnode
Cnode

Reputation: 557

Where to set cookie in Isomorphic Redux Application?

I have 3 general questions about redux and isomorphic application:

Thanks in advance.

Upvotes: 15

Views: 7138

Answers (4)

evanjmg
evanjmg

Reputation: 3790

Cookies are synchronous - you can either hydrate and subscribe to your store or make a meta reducer which wraps around the reducer before it is added to createStore. Here's quick example of both below:

//first option
 const Cookie = require('js-cookie');
const loadState = (key) => Cookie.getJSON(key);
const saveState = (nextState, key) => Cookie.set(key, nextState);
const persistedState = loadState('todos');
const store = createStore(
  todoApp,
  persistedState
);

store.subscribe(throttle(() => {
  saveState({
    todos: store.getState().todos,
  }, 'todos');
}, 1000));

//second option - meta reducer
// usage  
    const Cookie = require('js-cookie');

    export function cookieMeta (
      key: string,
      reducer: any,
      expiry: Date | number = 365,
      path: string = '/',
      domain: string = window.location.hostname): Function {
      return function(state: any, action: any): any {
        let nextState = reducer(state, action);
        let cookieState = Cookie.getJSON(key);

        if (action.type.includes('DELETE')) {
          Cookie.remove(key);
        } else if (!nextState && cookieState || action.type === '@@redux/INIT') {
          nextState = cookieState;
        } else if (nextState && nextState !== cookieState) {
            Cookie.set(key, nextState, { expires: expiry, path: path, domain: domain, secure: process.env.local });
        }
        return nextState;
      };
    };
// how to implement the meta reducer
import { todos } from './todos';
import { cookieMeta } from './middleware/cookieMeta';
export function TODOS_REDUCER (state: any, action: any) {
    return cookieMeta('todos', todos)(state, action);
}
export const todoApp = combineReducers({ todos: TODOS_REDUCER  })

Upvotes: 0

Cnode
Cnode

Reputation: 557

I managed to get a really neat app structure. Here's what I found for each questions:

  • I only share between my client and front-end server the API server token via cookies. Each time the client request the site. The front-end server calls the API server to validate the session. If these servers are on the same network it's really fast (< 5ms). I also prefetch some useful data for the client on the server before the initial render. I manage to get my application loaded and ready (javascript loaded) in the client in 600ms. It is pretty decent.

  • The action of storing the cookie is in my actions creators. As Ethan Clark said, we must keep reducers pure. It's much more easier to test.

  • I still dispatch the redirect in my signin creator once the user is authenticated. I guess it's easier to test than to dispatch the action after the promise resolution in component or elsewhere.

In fact, keeping this in mind allows us to have an app really easy to test (expect for the actions creator where you must have ton of spies).

Hope it will help someone.

Thanks for participating.

Upvotes: 6

Chris Geirman
Chris Geirman

Reputation: 9684

You should probably break your questions up into three different stack overflow questions since they're all a little different.

I agree with Ethan, your reducers should be pure with no side effects. That's the goal (aka best practice) anyway. However, Ben Nadel has been exploring questions along these lines and suggests creating a workflow layer to manage business logic rather than placing that burden on the store. You should check out his Managing Locally Cached Data with Redux in AngularJS article for more information about that.

Upvotes: 2

Ethan Clark
Ethan Clark

Reputation: 126

Question 2: you should execute cookie storing in your action creator. Reducers must remain pure functions.

I'm really sorry that I don't know the answers to 1 & 3, but I hope that this helps!

Upvotes: 4

Related Questions