Where to set cookie in Isomorphic Redux Application? - javascript

I have 3 general questions about redux and isomorphic application:
What is the best way to share 'runtime' data between client and server?
For instance, when the user logged in a distant API, I store the session object in cookies. In that way, next time the client requests my front-end, the front-end server can read the cookies and initialize the redux store with it's previous session. The downside of this is that the client HAS to validate/invalidate the session on boot (eg in componentDidMount of the root component).
Should I request the session server side rather than read it from cookies?
Where should I execute the operation of cookie storing, in action creators or in reducers? Should I store the cookie in my reducer that handle the user session?
Where should I execute the operation of redirect the user (via react-router)? I mean when my user is successfully logged in, from where should I dispatch the redirect action (from the loginActionCreator once the login promise is resolved?, somewhere else? )
Thanks in advance.

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.

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!

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.

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 })

Related

API Request Returns Two Objects: How Do I Separate State in Redux with One Reducer?

I have an API call, that is returning two objects: data and pageOutput.
{
data: "[{"title":["Travis Jackson HOF 1931 W517 # 12 - SGC 50"],"image":["https://i.ebayimg.com/00/s/MTYwMFg5NzY=/z/uU8AAOSwMtdd3ZXo/$_1.JPG"],"itemURL":["https://rover.ebay.com/rover/1/711-53200-19255-0/1?ff3=2&toolid=10044&campid=5338164673&customid=vintagebaseball&lgeo=1&vectorid=229466&item=133253226463"]"
pageOutput: "{"pageNumber":["1"],"entriesPerPage":["100"],"totalPages":["2"],"totalEntries":["194"]}"
}
I have a reducer that is fetching the data from my API and storing data in a a state called 'baseball' like this. Note it is just storing the 'data' piece from my API call.
const baseballReducer = (state = [], action) => {
switch (action.type) {
case "INIT_BLOGS":
return action.data;
export const initializeBaseball = () => {
return async dispatch => {
const baseballCards = await baseballService.getAll();
dispatch({
type: "INIT_BLOGS",
data: JSON.parse(baseballCards.data)
});
};
};
I need to get the pageOutput into a separate state. Now i know that i could create a separate page reducer, make a call to the api and store that pageOutput data in a new state. However, i think that it would be bad practice to make two different calls to the API?
Is there a better way? I was thinking of just storing all of the data (including the pageOutput) in the baseball state, but then I'm not sure if this is best practice or not, especially considering that I have a Pagination control that needs the updated active page to requery the database. Hope this is clear enough--happy to provide any additional code.
There is no need to make two api calls as you already have all data available in a single api call.
Basically you can:
Have two reducers listening to the same dispatched action, each reducer receiving a different portion of the total api response by destructuring the payload to the relevant property (data / pageOutput)
Dispatch two separate actions altogether, one for each reducer.
It doesn't really matter to the app which one you use, so imo it comes down to personal taste.
If you have control over the backend api, I would probably handle pagination a bit differently. Pagination state can be handled fully at the client, and the api would just respond to the client's request (limit / offset - or pageNr, perPage query parameters depending on your taste).
Even though you could therefore return meta data in the api response, you wouldn't need to depend on the api response to manage your state (frontend state change from page1 -> page2 would depend on clicking page2 and configurations defined by the client, not on the response of the api).

Is it recommended to have a mobx Store for each page?

I am building a single page application with Reactjs and MobX at the frontend (port 3000) and Nodejs and Express at the backend (API, port 4000). I am new to both, MobX and Reactjs and I am trying to set up a well-structured project.
My question is: Is it okay to have a Store for each view?
For example, I have a UserStore which stores the Session information and takes care of the login and logout of the user within the platform. However, after Logging in, I want to redirect the user to the dashboard page. This dashboard page must retrieve information regarding the user, but also it must contact the API and retrieve some data (i.e. Some Todos).
This is how I would do it:
This is the login function in which the redirection to Dashboard is made:
*UserStore.js*
[...]
import navigationStore from './NavigationStore';
[...]
login = async (user) => {
try {
const res = await axios.post('/session/login', {
username: user.username,
password: user.password
});
this.saveUser(res.data);
navigationStore.push('/dashboard');
} catch (error) {
[...]
}
}
And, then, I have created a DashboardStore.js which has the following code:
*DashboardStore.js*
[... imports and initializations ...]
class Store {
#observable todos = null
constructor() {
this.getDashboard();
}
#action('Load dashboard') getDashboard = async () => {
const res = await axios.get('/api/dashboard/', {});
this.todos = res.todos
}
}
const DashboardStore = new Store();
export default DashboardStore;
But this would mean that I'd end up doing another Store for the Todos page and another Store for whatever page I'd need.
In NodeJs you can make a controller for each class and there's nothing weird about it. However, I'm not sure that's how it works on MobX.
It depends on the complexity of your app. I wouldn't create a store for each view or concern, but you could create two, like the MobX docs recommend: https://mobx.js.org/best/store.html.
I'm working on a bigger project right now, and we started with a single store for everything. Obviously, it grew a lot as we kept adding functionality, so I think we might split it at some point to reduce complexity.

Where should things like the current state of an async action be stored in a react-redux application?

I have a login popup which maps a 'isLoggingIn' boolean to the redux store. When a login request action is dispatched a saga intercepts the action and sends another action that the login is processing, the reducer will take that in and set the 'isLoggingIn' boolean to true.
My store:
export interface AppState {
playerToken:string,
loginOpen: boolean,
loginProcessing: boolean
}
The login saga:
function* loginUser(action: any) {
yield put({ type: (LOGIN + PROCESSING) });
try {
const response = yield call(apiCall, 'api/token', 'POST', { username: action.payload.username, password: action.payload.password });
if (response)
{
yield put({ type: (LOGIN + SUCCESS), payload: response.data });
}
catch ({ statusCode }) {
if (statusCode === 401) {
yield put({ type: (LOGIN + FAIL), payload: { error: "Invalid username or password" } })
}
console.log(statusCode);
}
}
Once the saga is done with the login if there's an error it dispatches an action which the reducer sets to a 'loginError' string in the store and sets the isLoggingIn to false, otherwise isLoggingIn is set to false and the user login id is set which prompts the popup to hide itself (i.e. isVisible={this.props.playerToken == undefined).
This seems insanely complicated but I'm not sure how to break this down using Redux principles. I feel strongly the isProcessingLogin should be part of the components state, but the component has no real idea what's going on after it sends the login attempt event and there's no way for it to ever know unless it's listening on for something in the props.
It gets much worse with the various crud operations which need to happen and the various 'isCreatingXModel' booleans which have to be set to true/false in the store and mapped correctly in components.
Is this how redux is supposed to work or am I over using it in places it doesn't belong?
If this is how redux is supposed to be used what are its benefits exactly? I've read online a lot about things which make sense like having a single point of truth, but they can all be done without the crazy redux bloat, I've read people say not to use redux until you need it but that means I'm going to be doing api calls in two conceptually separate areas of code when redux is integrated whenever I 'need it', finally one of the biggest advantages I see purported by advocates is its ability to rewind and move forward in time, which is great but it won't work in any live application which connects to a database in the backend it manipulates unless as part of rewinding there's an undo last api call action.
Keep in mind that these are all entirely my opinions.
1. You might not need sagas (or thunk or other 'async' redux plugin)
Remember that redux is state management only. The API calls can be written in vanilla javascript with or without redux. For example: here's a basic replication of your flow without sagas:
e.g.
import { setLoadingStatus } from './actions'
import { store } from './reducers' // this is what is returned by a createStore call
export function myApiCall(myUrl, fetchOptions) {
store.dispatch(setLoadingStatus('loading'))
return fetch(myUrl, fetchOptions)
.then((response) => {
store.dispatch(setLoadingStatus('succeeded', data))
// do stuff with response data (maybe dispatch a different action to use it?)
})
.catch((error) => {
store.dispatch(setLoadingStatus('failed', error))
// do stuff
})
}
Note the use of store.dispatch. There's an interesting notion in React-Redux that you can only dispatch actions with mapDispatchToProps, but fortunately, that's not true.
I replaced your multiple actions with one that takes a state and optional data. This'll reduce the number of actions and reducers you need to write, but your action history will be harder to read. (Instead of three distinct actions, you'll only have one.)
2. You might not need redux.
The example function above could look basically identical if you weren't using redux -- imagine replacing the store.dispatch calls with this.setState calls. In the future, when you added it, you'd still have to write all the reducer, action, action creator boilerplate, but it would be only slightly more painful than doing it from the start.
As I said above, I usually go with Redux when working with React the built-in state management is has a bad mental map with any sort of large app.
There are two opposing rules of thumb:
use redux for state that needs to be shared between components
use redux for all state and get a single source of truth
I tend to lean to the second one. I hate hunting down errant pieces of state in the leaves of a large React component tree. This is definitely a question with no "correct" answer.

Difference Between a Redux middleware and reducer accepting all actions

In my redux project, I want to check something ( for example network connection ) in every action dispatch. Should I implement using a reducer which accepts all type of actions( without type checking ) as given below
export default (state = defaultState) => ({
...state,
neworkStatus: navigator.onLine
})
or with a middleware.
const NetworkMiddleware = store => next => (action) => {
const result = next(action)
const state = store.getState()
if (navigator.onLine && !state.NetworkDetector.networkStatus) next({ type: 'NETWORK_SUCCESS' })
if (!navigator.onLine && state.NetworkDetector.networkStatus) next({ type: 'NETWORK_ERROR' })
return result
}
export default NetworkMiddleware;
what is the difference between these two implementations
It provides a third-party extension point between dispatching an
action, and the moment it reaches the reducer. People use Redux
middleware for logging, crash reporting, talking to an asynchronous
API, routing, and more.
I think it would be better to use a middleware to analyse network activity. Read these Redux docs for further information.
A middleware in redux intercepts actions and performs some specific activity before it goes to the reducer to update the state. Middleware is meant to perform such actions without making the changes to the state in store. If you perform such tracking or modification by writing a reducer, you end up maintaining a state in the store for this activity which may have nothing to do with your component update or re-rendering. This is not a good practice I suppose and doesn't go as per the framework design. So it is better to achieve it via use of a middleware.

ReactJS: How to constantly check if token in localstorage has expired?

In ReactJS, is there a way to constantly check to see if the token saved in the localstorage has expired? If it has expired, would like to remove the token.
Came across the following but doesn't it only get triggered when the page gets reloaded?:
window.onbeforeunload = function() {
//remove token
return '';
}
The following assumes you are using redux... you can create a middleware that will trigger an action when a token expires.. this will allow for you to handle a reducer downstream. The redux approach is mainly because Redux is currently the most popular state management solution used with React.
// export the action type used as a const, so it can be imported for
// the reducer to listen for... The export const/key is more convenient
// then you can use a more namespaced string value to help prevent collisions
export const TOKEN_EXPIRED = 'tokenExpiredMiddleware_TokenExpired';
// redux middleware pattern (store) => (next) => (action) => result;
export default function expiredTokenMiddleware(store) {
// here we only care about a handle to the store for store.dispatch
// start checking every 15 seconds, probably want this configurable
setInterval(checkToken.bind(null, store), 15000);
// return a pass-through for the plugin, so other plugins work
return (next) => (action) => next(action);
}
// export checkToken for testing... etc
// should probably be a separate module
export function checkToken(store) {
var tokenId = ''; // TODO: Identify token
var isExpired = true; // TODO: replace with function to get current state
if (isExpired) {
store.dispatch({
type: TOKEN_EXPIRED,
payload: { tokenId },
});
}
};
Then, when you do your createStore, you can just add this middleware which will emit the appropriate action, and you can handle it in your appropriate reducer... I do something similar for the window's resize/scroll events so that my size/position is always set.
This is using ES6+ syntax, since you're using React I think that is a fair assumption
Since you cannot use code running on the user's machine for security relevant use cases anyway, why not check the token only when it is used?
At some point you most likely load the token from local storage and use it to e.g. authenticate the session. Why not first check its validity then before using it?
This saves you the trouble of having an ongoing activity that checks the token, bundles related functionality and most likely reduces the complexity of your code.
After all, the token won't do any harm by just being stored in the browser's storage without being used, would it?

Categories