ReactJS redux subscribe statements - reactjs

Is store.subscribe( () => {} ) the only way to watch for changes in the Redux stores?
I fetch my users asynchronously and by the time the app is loaded the Users store is most likely empty so I have to do something like this:
store.subscribe( () => {
var users = store.getState().users;
if( users.length > 0 ) {
this.setState({ ... })
}
});
Now I have 3 or 4 components I have to use the same logic in each of them. My question: is there a more elegant solution to this problem? Some way to listen to the changes in Redux on a 'global level'?

Two answers here.
Yes, store.subscribe() is the only way to watch the store for changes (with the nitpicking exception that the store also implements Symbol.observable, so you if you're using observables you could also subscribe that way).
However, the connect function from the official React-Redux already handles the process of store subscriptions and extracting data that your components need. Don't write store subscription logic yourself - use connect. (I wrote a long comment on Reddit explaining why you should use React-Redux instead of manually subscribing .)

Related

React Native Touch events responding very slow

I was creating a very simple first Native app in React but my app is responding very slow to events such as touch
As suggested I have implemented several things like implementing FlatList instead of mapping and so on..
The two things which i think i might be doing wrong is updating state in
componentDidUpdate() {
var updateCoinData = [...this.props.cryptoLoaded];
socket.on('trades', (tradeMsg) => {
for (let i=0; i<updateCoinData.length; i++) {
if (updateCoinData[i]["short"] == tradeMsg.coin ) {
//Search for changed Crypto Value
updateCoinData[i]["perc"] = tradeMsg["message"]["msg"]["perc"]
updateCoinData[i]['mktcap'] = tradeMsg['message']['msg']["mktcap"]
updateCoinData[i]['price'] = tradeMsg['message']['msg']['price']
//Update the crypto Value state in Redux
this.props.updateCrypto(updateCoinData);
}
}
})
or maybe calling setState too often in my child component.
If anyone wants reference this is the link to my repository
https://github.com/irohitb/Crypto
These are the two files where most of the operations are happening
Child Repository
Parent Repository
I know it is probably too much to ask but can someone help me/suggest me how I can improve its performance.
There is a 'structural error', redux abusing. Not directly related to question (peformance) however it can have some indirect relation.
You're processing state/data locally while it's a reducers responsibility.
You should prepare payload from socket data and dispatch redux action/message. Reducer should create copy of prev state (use slice() to copy array, spread operator '...' is slow), search for matching id, update it and return as new state.
Main idea of reducer is extracting logic of state transitions to be simply testable. What can you test when only storing/receiving ready data set?

Is it OK for a reducer to listen to other actions?

At the moment I'm creating actions and then a reducer to handle different parts of my app... the different domains.
My app lists classes and pupils.
Currently I have an action that the app has loaded so that I know when to remove the loading spinner, I have actions for classes and pupils. My problem is that I find I need to execute several actions in a row and am not sure if this is a valid way to use redux.
Here is an example function that dispatches several actions after the data is loaded:
/**
* Callback used by readAppData.
*
* #param object ioResult An object: {success: boolean, errorObj?: object, data?: string}
*/
dataLoaded(ioResult: Object) {
if (ioResult.success === true) {
this.props.dispatch(appActions.appHasLoaded());
if (ioResult.data !== '') {
const jsonData = JSON.parse(ioResult.data);
this.props.dispatch(classActions.replaceClasses(jsonData.classes));
this.props.dispatch(pupilActions.replacePupils(jsonData.pupils));
}
} else if (ioResult.errorObj.code === 'ENOENT') { // File doesn't exist.
writeAppData('', this.dataSaved);
} else { // Some other error.
this.props.dispatch(appActions.appHasErrored());
}
}
I was thinking about putting the jsonData.classes and jsonData.pupils into the appActions.appHasLoaded() call and just have a new case for APP_HAS_LOADED in my classes and pupils reducers.
Is this a better approach? To reduce it down to one action? Having separate actions makes it easy to see what is happening... maybe in 6 months time I will have to look through my code to work out exactly what happens on APP_HAS_LOADED if I use it in other reducers. Also the data that is loaded on app start is going to expand beyond just classes and pupils so if I don't combine the calls there could soon be many more dispatches to make - maybe I can store the data in separate files and load each one one at a time which would also fix my problem of having to call mutiple actions in a row.
Is it OK to call dispatch multiple times?
Yes, you can.
From Redux creator Dan Abramov:
Many reducers may handle one action. One reducer may handle many actions.
Referenced in Redux Docs
also, there is a conversation on github about this.

Global variables in React

I know Redux solves this but I came up with an idea.
Imagine I have an app that gets some JSON on start. Based on this JSON I'm setting up the environment, so let's assume the app starts and it downloads an array of list items.
Of course as I'm not using Redux (the app itself is quite simple and Redux feels like a huge overkill here) if I want to use these list items outside of my component I have to pass them down as props and then pass them as props again as deep as I want to use them.
Why can't I do something like this:
fetch(listItems)
.then(response => response.json())
.then(json => {
window.consts = json.list;
This way I can access my list anywhere in my app and even outside of React. Is it considered an anti-pattern? Of course the list items WON'T be changed EVER, so there is no interaction or change of state.
What I usually do when I have some static (but requested via API) data is a little service that acts kind like a global but is under a regular import:
// get-timezones.js
import { get } from '../services/request'
let fetching = false
let timez = null
export default () => {
// if we already got timezones, return it
if (timez) {
return new Promise((resolve) => resolve(timez))
}
// if we already fired a request, return its promise
if (fetching) {
return fetching
}
// first run, return request promise
// and populate timezones for caching
fetching = get('timezones').then((data) => {
timez = data
return timez
})
return fetching
}
And then in the view react component:
// some-view.js
getTimezones().then((timezones) => {
this.setState({ timezones })
})
This works in a way it will always return a promise but the first time it is called it will do the request to the API and get the data. Subsequent requests will use a cached variable (kinda like a global).
Your approach may have a few issues:
If react renders before this window.consts is populated you won't
be able to access it, react won't know it should re-render.
You seem to be doing this request even when the data won't be used.
The only downside of my approach is setting state asynchronously, it may lead to errors if the component is not mounted anymore.
From the React point of view:
You can pass the list from top level via Context and you can see docs here.
Sample of using it is simple and exists in many libraries, such as Material UI components using it to inject theme across all components.
From engineering concept of everything is a trade of:
If you feel that it's gonna take so much time, and you are not going to change it ever, so keep it simple, set it to window and document it. (For your self to not forget it and letting other people know why you did this.)
If you're absolutely certain they won't ever change, I think it's quite ok to store them in a global, especially if you need to access the data outside of React. You may want to use a different name, maybe something like "appNameConfig"..
Otherwise, React has a feature called Context, which can also be used for "deep provision" - Reference

Redux+Websockets: Why manage this using middleware?

I've been reading about the best way to integrate WebSockets into a React/Redux app, and I'm finding answers but with some sentence along the lines of "The best place for websocket implementation is usually middleware."
My question is WHY this is preferred? What is the benefit of doing this vs setting up the websocket/having listeners dispatch actions in the outer App-level React container (in componentWillMount for instance)?
This seems like it would be equivalent in terms of lasting throughout the lifecycle of the app, etc. What am I missing here?
There are couple of pros with placing such logic in middle-wares instead of actual components.
The main reasons in my opinion are:
Each connected component will instantiate a WebSocket or you will
need a global declaration of the connection which will be
independently from the store, in other words, not part of the redux
flow.
Middle-Wares have access to the store and are part of the redux
flow.
You also get access to the entire store, hence you can pass
forward more data then initially dispatched.
You decouple the components from the web-socket logic, hence you can
centralize your code and reuse your components.
All in all, there are no special reasons that are specific regarding using web-sockets with middle-wares, using middle-wares have great advantages in general.
Edit
As a followup to your comment
How would you suggest managing a case where you might want a
particular component to initialize a websocket connection, but want to
manage whether it's already connected, etc... would it be just as
simple as having a flag in the store that says it's connected or is
there a better approach?
As i said, I wouldn't initialize a web-socket connection within a component, rather I would do it in the entry point of my application. like index.js for example.
If your concern is to make sure you won't try to connect when there is already a connection, then a simple socketStart method that get invoked at the point when you create the store and initialize all your App data, you can pass it a callback that will do the rendering and store update through dispatch.
A simple example (keep in mind this is a pseudo code ):
Our Socket-start Method:
export function socketStart(store, callback) {
// this is only a pseudo code!
// register to server -> client events
_socketClient.someFunction = (data) => {
store.dispatch({ type: "Server_Invoked_Some_Function", data });
}
_socketClient.someFunction2 = (data) => {
store.dispatch({ type: "Server_Invoked_Some_Function2", data });
}
// connect to the server via the web-socket client API
_socketClient.connect(() => callback());
}
We can use it in our index.js file:
let store = createStore(
// your reducers here...
// ...
applyMiddleware(socketMiddleware) // our web socket middleware
)
// the callback will invoked only if the connection was successful
// the React-Dom's render function is our callback in this case
socketStart(store, () => { render(<App />, document.getElementById("root")); });
With middleware, you can easily unfold/relay messages between Redux and Web Socket. Also, you can use Redux middleware without React, that means you can write API using Redux on server-side code (probably with Redux saga).
I would agree lifetime management as a React component is easier than a Redux middleware. But if you want to reconnect (destroy/recreate), you will need to use key props to make the reconciler to consider it as a new object, which is a little bit weird.
You can look at redux-websocket-bridge, which unfold Web Socket messages into Redux actions, and relay Redux actions to Web Socket.
On your Web Socket server, you send an action:
ws.on('connection', conn => {
conn.send(JSON.stringify({
type: 'GREETING',
payload: { now: Date.now() }
}));
});
You will get the GREETING action on your Redux reducer. And vice versa, when you want to relay an action to Web Socket, you mark your action with meta.send with true:
this.props.dispatch({
type: 'SIGN_IN',
meta: { send: true },
payload: { authToken: '...' }
});

Where should I load data from server in Redux + ReactJS?

For example I have two components - ListOfGroupsPage and GroupPage.
In ListOfGroupsPage I load list of groups from the server and store it to the state.groups
In route I have mapping like ‘group/:id’ for GroupPage
When this address is loaded, the app shows GroupPage, and here I get the data for group from state.groups (try to find group in state via id).
All works fine.
But if I reload page, I'm still on page /group/2, so GroupPage is shown. But state is empty, so the app can't find the group.
What is the proper way to load data in React + Redux? I can see this ways:
1) Load all data in root component. It will be very big overhead from traffic side
2) Don't rely on store, try to load required data on each component. It's more safe way. But I don't think that load the same data for each component - it's cool idea. Then we don't need the state - because each component will fetch the data from server
3) ??? Probably add some kind of checking in each component - first try to find required data in store. If can't - load from the server. But it requires much of logic in each component.
So, is there the best solution to fetch data from server in case of usage Redux + ReactJS?
One approach to this is to use redux-thunk to check if the data exist in the redux store and if not, send a server request to load the missing info.
Your GroupPage component will look something like
class GroupPage extends Component {
componentWillMount() {
const groupId = this.props.params.groupId
this.props.loadGroupPage(groupId);
}
...
}
And in your action...
const loadGroupPage = (groupId) => (dispatch, getState) => {
// check if data is in redux store
// assuming your state.groups is object with ids as property
const {
groups: {
[groupId]: groupPageData = false
}
} = getState();
if (!groupPageData) {
//fetch data from the server
dispatch(...)
}
}
I recommend caching the information on the client using localstorage. Persist your Redux state, or important parts of it, to localstorage on state change, and check for existing records in localstorage on load. Since the data would be on the client, it would be simple and quick to retrieve.
The way I approach this is to fetch from the server straight after the store has been created. I do this by dispatching actions. I also use thunks to set isFetching = true upon a *_REQUEST and set that back to false after a *_SUCCESS or *_FAILURE. This allows me to display the user things like a progress bar or spinner. I think you're probably overestimating the 'traffic' issue because it will be executed asynchronosly as long as you structure your components in a way that won't break if that particular part of the store is empty.
The issue you're seeing of "can't get groups of undefined" (you mentioned in a comment) is probably because you've got an object and are doing .groups on it. That object is most likely empty because it hasn't been populated. There are couple of things to consider here:
Using ternary operators in your components to check that someObject.groups isn't null; or
Detailing in the initialState for someObject.groups to be an empty array. That way if you were to do .map it would not error.
Use selectors to retrieve the list of groups and if someObject.groups is null return an empty array.
You can see an example of how I did this in a small test app. Have a look at specifically:
/src/index.js for the initial dispatch
/src/redux/modules/characters.js for the use of thunks
/src/redux/selectors/characters.js for the population of the comics, series, etc. which are used in the CharacterDetails component

Resources