I have the following situation: The user enters his credentials and clicks a Login button. An API call is done in the action creator via redux-thunk
. When the API call was successful, another action is dispatched containing the response from the server. After the (successful) login I want to store the users session id in a cookie (via react-cookie
).
Action creator
export function initiateLoginRequest(username, password) { return function(dispatch) { dispatch(loginRequestStarting()) return fetch('http://path.to/api/v1/login', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ username: username, password: password }) }) .then(checkStatus) .then(parseJSON) .then(function(data) { dispatch(loginRequestSuccess(data)) }) .catch(function(error) { dispatch(loginRequestError(error)) }) } } export function loginRequestSuccess(user) { return { type: ActionTypes.LOGIN_REQUEST_SUCCESS, user } }
Reducer
export default function user(state = initialState, action) { switch (action.type) { case ActionTypes.LOGIN_REQUEST_SUCCESS: cookie.save('sessionId', action.user.sid, { path: '/' }) return merge({}, state, { sessionId: action.user.sid, id: action.user.id, firstName: action.user.first_name, lastName: action.user.last_name, isAuthenticated: true }) default: return state } }
Right now the reducer responsible for LOGIN_REQUEST_SUCCESS saves the cookie. I know the reducer has to be a pure function.
Is saving a cookie in the reducer violating this principle? Would it be better to save the cookie inside the action creator?
But...that means in most of off applications the only data what I should store in Redux store is the logged in user data. That is the only entity which attributes influences all the screens (role, login name, user settings). Usual applications do not need to remember entered data between screens, cache, history....
The point of redux is immutable data flow, cookies are more like a global variable. You could use the cookie store or local storage API to store data (see react-redux-persist ) but you wouldn't rely on it performance wise.
dispatch(action) Dispatches an action. This is the only way to trigger a state change. The store's reducing function will be called with the current getState() result and the given action synchronously.
Have a look at redux-persist.
You can persist/save your reducers (or parts of them) in LocalStorage
.
npm install --save-dev redux-persist
Create a component that wraps the persistence/rehydration logic.
AppProvider.js
import React, { Component, PropTypes } from 'react'; import { Provider } from 'react-redux'; import { persistStore } from 'redux-persist'; class AppProvider extends Component { static propTypes = { store: PropTypes.object.isRequired, children: PropTypes.node } constructor(props) { super(props); this.state = { rehydrated: false }; } componentWillMount() { const opts = { whitelist: ['user'] // <-- Your auth/user reducer storing the cookie }; persistStore(this.props.store, opts, () => { this.setState({ rehydrated: true }); }); } render() { if (!this.state.rehydrated) { return null; } return ( <Provider store={this.props.store}> {this.props.children} </Provider> ); } } AppProvider.propTypes = { store: PropTypes.object.isRequired, children: PropTypes.node } export default AppProvider;
Then, in your index.js
or file in which you set up the store
, wrap the rendered components in your new AppProvider
.
index.js
... import AppProvider from 'containers/AppProvider.jsx'; ... render(( <AppProvider store={store}> ... </AppProvider> ), document.getElementById('App'));
This will serialize your user
reducer state to LocalStorage
on each update of the store/state. You can open your dev tools (Chrome) and look at Resources
=> Local Storage
.
I'm not sure if this is the "right" way, but that's how my team is persisting the logged user in the Redux app we built:
We have a very default architecture, an API ready to receive requests in one side, and a React/Redux/Single Page app that consumes this API endpoints in the other side.
When the user credentials are valid, the API's endpoint responsible for the login respond to the app with the user object, including an access token. The access token is latter used in every request made by the app to validate the user against the API.
When the app receives this user information from the API two things happen: 1) an action is dispatched to the users reducer, something like ADD_USER
, to include this user in the users store and 2) the user's access token is persisted in the localStorage
.
After this, any component can connect to the users reducer and use the persisted access token to know who is the logged user, and of course if you have no access token in your localStorage
it means the user is not logged in.
In the top of our components hierarchy, we have one component responsible to connect to the users reducer, get the current user based on the access token persisted in the localStorage
, and pass this current user in the React's context. So we avoid every component that depends on the current user to have to connect to the users reducer and read from the localStorage
, we assume that this components will always receive the current user from the app's context.
There are some challenges like token expiration that adds more complexity to the solution, but basically this is how we are doing it and it's working pretty well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With