I am trying to implement a socket based react(redux) application. the socket events has to be dispatched to the store so the reducers would act upon it.
Now the way I think is keep the store in one file and refer to that from Socket events File and whenever socket messages are received then emit an event to store via socket.dispatch
in store.js
import { createStore } from 'redux';
let store = createStore(appStore);
export default store
in SocketJs file
import store from './store';
import io from socketio.client;
let Socket = {
createSocketIoConnection = function(){
var socket = io(url, {reconnection: false, transports: ['websocket']});
socket.on('data', this.emitToStore);
},
emitToStore: function(data){
store.dispatch();
}
}
the same store is used in main file ad passed as props to Provider
import store from './store';
<Provider store={store}>
<App> ...
</Provider>
Here I am not using any middleware. Is this a right approach.
No, you don't have to use middleware, but middleware is the right place to put this kind of behavior. In fact, there's already many existing websocket middlewares you can use.
Related
I have a react app that uses sessions with an express server using REST API.
In my Redux store(on the front end) I store "isLoggedIn" in the redux state. Based on that property I show either the login page , or the home page. Now when I open the website, the initial redux "isLoggedIn" state is false, so the question is, how do I check if the user is actually logged in. I am thinking about sending a request to the endpoint for fetching the data I need in the homepage, and if the user is not logged in, I would get an 401 response and then show the login page. Is this the correct approach?
Before I get into how you could persist your redux state I would advise(Note This is a personal view)
Going through the boiler plate code of setting that up is really unnecessarily long
Best way out for this is using express-session whereby the cookie is persisted for as long as you gave it e.g if you set cookie to be 3days it will stay for 3days
I so believe that it's industry standard working with httpOnly cookie for session handling as this is secure avoiding XSS attacks and CSRF attacks
Either way below is your solution to persisting Redux Store
So with persisting your redux state you can look into something like this
First run npm install redux-persist or yarn add redux-persist
Now time to create your redux store
So now when you create your redux store with createStore you want to pass your createStore function a persistReducer Once your store is created, pass it to the persistStore function, which will make sure that your redux state is persisted
Implementation below
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import autoMergeLevel2 from 'redux-persist/lib/stateReconciler/autoMergeLevel2';
import rootReducer from './reducers';
//Import above comes from a file **reducers** which has the combineReducers
// basically based on this code snippet
// I have my combineReducer object in another folder hope that it
//makes sense. Avoided to write it here to keep code short to show
//more of setup in store persisiting
const persistStoreConfigs = {
key: 'root',
storage: myAppStorage,
stateReconciler: autoMergeLevel2
};
const persistantReducer = persistReducer(persistStoreConfigs, rootReducer);
export const store = createStore(persistantReducer);
export const persistor = persistStore(store);
Now passing the store to the App level
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
// This are coming from the store code above that we made
import { persistor, store } from './store';
// This are basically your Custom Componentd
import { HomePage, LoadingView } from './components';
const App = () => {
return (
<Provider store={store}>
// the loading and persistor props are both required!
// Noting that the LoadingView is a custom component
// But the persistor isn't
//The main aim of this two props is to delay the rendering of your app UI until the persited state is retrieved hence the loading screen component
<PersistGate loading={<LoadingView />} persistor={persistor}>
<HomePage />
</PersistGate>
</Provider>
);
};
export default App;
What is the best pattern for a large multi-component ReactJS app to integrate with a socket io back-end server?
Here are some of the requirements
The React application must connect to the backend (a server using a Flask-based socket.io implementation) upon login. The connection must be torn down on logout
The connection should be managed from a central place (as in I don't want every component to connect/disconnect to socket-io). This should also be a place to manage life-cycle of the socketio connection (disconnect, reconnect, etc).
Back-end will send async updates (a.k.a statistics for various components). These stats must be handled by the common socketio handling instance and pushed into redux store. The message body will have enough information to demux the messages.
The components themselves, should (preferably) not know about socket-io, they operate on the redux state.
Components can dispatch actions, that could potentially result in sending out a socketio message (to back-end).
[I don't have a use-case where a client needs to talk to another client.]
Few questions:
What is the right way to design this? At what point should I connect to socket-io?
I have a basic-layout component which is used by all pages. Is this the right place to hook up socket-io call? However, I see that this component is unloaded/loaded for each page. Somehow, this doesn't feel like the right place
I have seen some examples where every page opens a socketio connection. I am not sure if this is the right model?
I think you should probably store your socket on a Context, so that you can create your socket on your index.js file, like I did here :
import React from "react";
import App from "./App";
import socketIOClient from "socket.io-client";
import { MainProvider } from "./context/MainContext.js";
const ENDPOINT = "http://127.0.0.1:1234"; //Your backend endpoint
ReactDOM.render(
<React.StrictMode>
<MainProvider value={{socket: socketIOClient(ENDPOINT)}}>
<App />
</MainProvider>
</React.StrictMode>,
document.getElementById("root")
);
Then as it is on the context, you can access it in any component, like that :
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import MainContext from "./context/MainContext"; //import your context here
const myComponent = props => {
const myContext = useContext(MainContext); //your context is stored in a prop
return (
<div></div>
);
};
myComponent.propTypes = {
};
export default myComponent;
Finally you can access your socket in the component by calling myContext.socket
Why should I use ApolloConsumer instead of importing directly the client in my module ?
From the doc I should do somthing like :
// Module A.js initiate client
const client = new ApolloClient({
// init cache, links, typeDefs...
});
export default client;
// Module index.jsx
import client from 'A';
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root'));
// Any other component not using Query or Mutation
const Other = () => (
<ApolloConsumer>
{
client => {
// use client
}
}
</ApolloConsumer>);
But why not just import client without ApolloConsumer ?
// Module OtherBis
import client from 'A';
const AltOther () => {
// do something with client
return <div></div>;
};
Probably for the same reasons why you shouldn't import store directly:
Similarly, while you can reference your store instance by importing it directly, this is not a recommended pattern in Redux. If you create a store instance and export it from a module, it will become a singleton. This means it will be harder to isolate a Redux app as a component of a larger app, if this is ever necessary, or to enable server rendering, because on the server you want to create separate store instances for every request.
In my opinion, ApolloConsumer component is created for support JSX just like react-relay for relay. It is not necessary. Actually I have never used the ApolloConsumer. Moreover there are hooks(eg. useQuery, useMutation, useSubscription) which can do everything. They just make a tool. Whether use it or not is up to you.
I somehow managed to get it work, but i think it is not good solution.
my main file is:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, hashHistory } from 'react-router';
import { Provider } from 'react-redux';
import routes from './router/routes';
import store from './store.js';
ReactDOM.render(
<Provider store={ store }>
<Router routes={ routes } history={ hashHistory } />
</Provider>,
document.getElementById('wrap')
);
my store.js is:
import { createStore } from 'redux';
import reducer from './reducers/index';
import initialState from './reducers/initial-state';
import connection from './lib/connection';
const store = createStore(reducer, initialState);
connection(store);
export default store;
and connection is:
import AppActions from '../actions/AppActions';
import io from 'socket.io-client';
export default function (store) {
const mainsocket = io.connect('$socketurl');
mainsocket.store = store;
mainsocket.actions = AppActions;
var socket;
mainsocket.on('namespaceinitialized', function(data) {
socket = io.connect("$socketurl/$namespace", {'autoConnect': false,'forceNew': true});
socket.connect();
socket.store = store;
socket.actions = AppActions;
socket.on('connect',function (data) {
//need store and actions
});
});
}
As you see i am using 2 connections, first for namespace initialize and then second (this is not important in question).
I dont like this "dirty" solution where i am attaching store and actions to mainsocket object and then passing them from mainsocket to socket, in order to have access to app actions on socket messages, and the question is what is right way?
Also in this code socket connects before app actions and store have initialized, i would like to socket connection be last thing but i don't know where to put it then?
I must say that i am don't writing my own application but adding functionality to existing open source one.
thank you.
I am using react-redux and I am having a issue where I loose my redux state when the page is refreshed.
Now before I go further on that this is the scenario, which I may be messing up.
Question one: Can I connect to more than one layout?
I have a dashboard and a "app" layout. Both have separate layouts. I connect both in the same manner:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
function mapStateToProps(state) {
return {
profile: state.profile,
child: state.child,
}
}
function mapDispachToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const LayoutApp = connect(mapStateToProps, mapDispachToProps) (DashboardLayout);
export default LayoutApp;
The dashboard connects just fine. I am able to hit reducers when i need to and update the store, however the dashboard links to the app for certain parts of data manipulation for you to play with. When it links I get the data in props as expected, however as soon as any page refreshes within the app layouts I loose the props being sent by maptoprops.
I have tried to combine into one master layout however that seems to have the same affect. I have also tried to save to state immediately when i first receive data but that seems to be lost as well upon refresh which makes me think it is resetting it.
Summary:
- DashboardLayout (connects to redux)
- AppLayout (connects to redux) however after a page refresh it looses props to the Applayout and needed data is gone.
Get to know redux-persist
https://github.com/rt2zz/redux-persist
You can install it using
npm i --save redux-persist
persistStore is the function that allows you to persist store.
import {persistStore} from 'redux-persist'
and autoRehydrate is the action that is performed whenever the state needs to be rehydrated
import {autoRehydrate} from 'redux-persist'
following is the structure that may be useful.
import {compose, applyMiddleware, createStore} from 'redux'
import {persistStore, autoRehydrate} from 'redux-persist'
// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(...),
autoRehydrate()
)
)
// begin periodically persisting the store
persistStore(store)
and then for your reducer
import {REHYDRATE} from 'redux-persist/constants'
//...
case REHYDRATE:
var incoming = action.payload.myReducer
if (incoming) return {...state, ...incoming, specialKey:
processSpecial(incoming.specialKey)}
return state
following are the methods which can be used to work out of redux-persist
persistor.pause() - pauses redux persist
persistor.resume() - resumes redux persist
persistor.purge() - deletes all persisted data
persistor.rehydrate() - calls reducer to rehydrate store
types of storage redux persist allows
// sessionStorage
import { persistStore } from 'redux-persist'
import { asyncSessionStorage } from 'redux-persist/storages'
persistStore(store, {storage: asyncSessionStorage})
// react-native
import {AsyncStorage} from 'react-native'
persistStore(store, {storage: AsyncStorage})
// web with recommended localForage
import localForage from 'localforage'
persistStore(store, {storage: localForage})
it is the most basic use redux-persist hope it helps.
You can connect to as many components as you want to same store properties - that's not issue. Just to note here, connect is listening for changes so when action changes store it would cause all components listening to changed store parts to re-render (or at least do computation and then compare shadowdom).
Store is stored only when running application - after page close/resfresh it's cleared.
There is good thread about persistence of store (eg. to local storage): Where to write to localStorage in a Redux app?
As mentioned in the previous answers storing the state in local storage is one possible solution however some other possible solutions are
Make sure your links use the Push API ie <Link to={"route"}/> (react-router) this will ensure the store is not removed between route changes
Set your initial state when you create your store
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
});
const initialState = {
todos: [{id:123, text:'hello', completed: false}]
};
const store = createStore(
rootReducer,
initialState
);
This will hydrate your reduce store when it is initialized
There are some other libs/frameworks/boilerplates that you could use that also implement server rendering with will render the HTML to the page on the initial render and then load React once the DOM has loaded some of this libs are
Next.js,Electroide,react-redux-universal-hot-example
Going down this route will add significant complexity to your app so you should weigh up the pros and cons before embarking down this route
I had this problem, so as somebody mentioned here, i used the localStorage.
In my reducer, i did like this:
const exampleKey = "_exampleKey"
const INITIAL_STATE = {
example: JSON.parse(localStorage.getItem(exampleKey))
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_TRUE':
localStorage.setItem( exampleKey, JSON.stringify(true));
return { ...state, example: true };
case 'SET_FALSE':
localStorage.setItem( exampleKey, JSON.stringify(false));
return { ...state, example: false};
default:
return state
}
Pointing INITIAL_VALUES.example to localStorage's item example ensure us to keep the correct value when the page reloads.
Hope it helps somebody.