Redux-thunk and redux-promise: empty payload in PENDING state - reactjs

So I have redux-thunk set up and when I call the updateUser(userInfos) action, it returns a promise to update the server with those infos.
But sequentially I need to update the local state with those infos too, and if I wait for UPDATE_USER_FULFILLED, the effect of my action is not instantaneous, which feels weird for the user.
I thought of reducing the UPDATE_USER_PENDING action to update the local state before the server response arrives with up-to-date data (which should be the same so no re-rendering), but unfortunately, the payload of UPDATE_USER_PENDING action is null :(
Ain't there a way to load it with some data, which would allow instant UI response before server return?
Could be something like:
export function updateUser(user) {
return {
type: UPDATE_USER,
payload: {
promise: userServices.update(user),
localPayload: user,
}
}
}
but I'm guessing this is not available?
For now, the only solution I see is to create a UPDATE_USER_LOCAL action which would do the local action in parallel with the server call. But this is not very elegant and much heavier.
Thanks for the help

Related

A more performance-friendly way to use setInterval() in componentDidMount when fetching data

i am struggling pretty hard here to find the right solution. Currently, I am using setInterval() to "poll" my server and retrieve an array of objects. To fetch the data, I am using axios. Here are the pertinent functions:
componentDidMount(){
this.timer = setInterval(() => [this.getData(), this.getCustData()], 1000);
}
componentWillUnmount(){
this.timer && clearInterval(this.timer);
this.timer = false
}
getData = () => {
axios.get('http://localhost:3001/api/v1/pickup_deliveries')
.then((response) => {
this.setState({
apiData: response.data
})
})
.catch((error)=>{console.log(error);});
}
getCustData = () => {
axios.get('http://localhost:3001/api/v1/customers')
.then((response) => {
this.setState({
custData: response.data
})
})
.catch((error)=>{console.log(error);});
}
The application is running so slow and often times, it will completely hang the server which makes the whole application unusable. Currently the array it's fetching has over 1000+ objects and that number is growing daily. If I fetch the data without polling the server, the feel of my application is night and day. I am not quite sure what the answer is but I do know what I am doing is NOT the right way.
Is this just the nature of mocking "polling" with setInterval() and it is what it is? Or is there a way to fetch data only when state has changed?
If I need to implement SSE or WebSockets, I will go through the hassle but I wanted to see if there was a way to fix my current code for better performance.
Thanks for hearing me out.
On the frontend side, my advice would be to not use setInterval, but use setTimeout instead.
Using setInterval, your app might send another request even if the response for previous request hasn't come back yet (e. g.: it took more than 1 second). Preferably, you should only send another request 1 second after the previous response is received.
componentDidMount() {
getData();
}
getData = () => {
fetch().then(() => {
updateState();
// schedule next request
setTimeout(getData, 1000);
});
}
You should also try to reduce the amount of updates that need to be done on the frontend, for example by reducing the number of the data.
But nevertheless, I think the most important is to rethink the design of your application. Polling huge JSON that is going to only grow bigger is not a scalable design. It's bad for both the server and the client.
If what you are trying to do is to have the client be notified of any changes in the server side, you should look into WebSocket. A simple idea is that the browser should establish a WS connection to the server. Upon any updates to the server, instead of sending down the whole data, the server should only send down the updates to the client. The client would then update its own state.
For example, let's say 2 users are opening the same page, and one user make changes by adding a new Product. Server will receive this request and update the database accordingly. It will also broadcast a message to all open WebSocket connections (except for the one connection that added the Product), containing a simple object like this:
{
"action": "INSERT",
"payload": {
"product": {
"id": 123123,
... // other product data
}
}
}
The other user will use this data to update its own state so it matches the server.

After axios call, how should I update the front-end? [I have 2 ways]

I have an array of todos. When I delete one of them, I succesfully delete it also from the database with DELETE call. However, I am not sure how to update the front-end. First way is changing the state by deleting the related todo.
onDelete(todo) {
axios.delete('api/todos/' + todo.id).then(res => {
var array = [...this.state.todos]; // make a separate copy of the array
var index = array.indexOf(todo)
array.splice(index, 1);
this.setState({todos: array}); // other state elemenets other than todos will not be affected
});
}
Other way is, making a new axios GET request to get all the todos from the database. (this will be an axios request inside an axios request)
onDelete(todo) {
axios.delete('api/todos/' + todo.id).then(res => {
// make an axios get request on api/todos
// then, set state with data in response.
});
}
Hence, which one is the better approach?
The best approach would be the second one, to call a get request once more. So that no matter what the change database has undergone, the UI will be an exact replica of the data from db instead of manually changing(deleting in this case) the list from UI. However there will be the delay of api response for UI updation in this case.

My flux store gets re-instantiated on reload

Okay. I'm kinda new to react and I'm having a #1 mayor issue. Can't really find any solution out there.
I've built an app that renders a list of objects. The list comes from my mock API for now. The list of objects is stored inside a store. The store action to fetch the objects is done by the components.
My issue is when showing these objects. When a user clicks show, it renders a page with details on the object. Store-wise this means firing a getSpecific function that retrieves the object, from the store, based on an ID.
This is all fine, the store still has the objects. Until I reload the page. That is when the store gets wiped, a new instance is created (this is my guess). The store is now empty, and getting that specific object is now impossible (in my current implementation).
So, I read somewhere that this is by design. Is the solutions to:
Save the store in local storage, to keep the data?
Make the API call again and get all the objects once again?
And in case 2, when/where is this supposed to happen?
How should a store make sure it always has the expected data?
Any hints?
Some if the implementation:
//List.js
componentDidMount() {
//The fetch offers function will trigger a change event
//which will trigger the listener in componentWillMount
OfferActions.fetchOffers();
}
componentWillMount() {
//Listen for changes in the store
offerStore.addChangeListener(this.retriveOffers);
}
retrieveOffers() {
this.setState({
offers: offerStore.getAll()
});
}
.
//OfferActions.js
fetchOffers(){
let url = 'http://localhost:3001/offers';
axios.get(url).then(function (data) {
dispatch({
actionType: OfferConstants.RECIVE_OFFERS,
payload: data.data
});
});
}
.
//OfferStore.js
var _offers = [];
receiveOffers(payload) {
_offers = payload || [];
this.emitChange();
}
handleActions(action) {
switch (action.actionType) {
case OfferConstants.RECIVE_OFFERS:
{
this.receiveOffers(action.payload);
}
}
}
getAll() {
return _offers;
}
getOffer(requested_id) {
var result = this.getAll().filter(function (offer) {
return offer.id == requested_id;
});
}
.
//Show.js
componentWillMount() {
this.state = {
offer: offerStore.getOffer(this.props.params.id)
};
}
That is correct, redux stores, like any other javascript objects, do not survive a refresh. During a refresh you are resetting the memory of the browser window.
Both of your approaches would work, however I would suggest the following:
Save to local storage only information that is semi persistent such as authentication token, user first name/last name, ui settings, etc.
During app start (or component load), load any auxiliary information such as sales figures, message feeds, and offers. This information generally changes quickly and it makes little sense to cache it in local storage.
For 1. you can utilize the redux-persist middleware. It let's you save to and retrieve from your browser's local storage during app start. (This is just one of many ways to accomplish this).
For 2. your approach makes sense. Load the required data on componentWillMount asynchronously.
Furthermore, regarding being "up-to-date" with data: this entirely depends on your application needs. A few ideas to help you get started exploring your problem domain:
With each request to get offers, also send or save a time stamp. Have the application decide when a time stamp is "too old" and request again.
Implement real time communication, for example socket.io which pushes the data to the client instead of the client requesting it.
Request the data at an interval suitable to your application. You could pass along the last time you requested the information and the server could decide if there is new data available or return an empty response in which case you display the existing data.

Electron/React/Redux - How to load a local file in an action creator?

I am working on an Electron/React/Redux app that uses NeDB for a local data store (though this problem is not really about NeDB I think). I can use ipcRenderer.send to make calls to load the local data store but I am unclear on how to do this in the context of a redux action creator since the way I get the data back is via the ipcRenderer.on listener.
An action creator that obviously doesn't work...
export function getData() {
ipcRenderer.send('getData');
return {
type: GET_DATA,
payload: // WHAT IS THE PAYLOAD?
}
}

Using Request/Reply in SAGA NserviceBus

I'm creating a SAGA in NServiceBus. This saga is handling some string which has to be transformed then validated and finally imported. These three actions are separate services. I want the actions as separate handlers in NServiceBus. I'm using the Request/Reply functionality in NServiceBus. Like this:
Bus.Send<TransformRequest>("Backend", m =>
{
m.string = string;
m.MessageId = messageId;
})
.Register(i =>
{
Console.WriteLine("transform finished")
});
The transformationHandler is as follows.
public void Handle(TransformRequest message)
{
var transformationResult = _transformationService.Transform(message.string);
var response = new TransformResponse()
{
string= transformationResult,
messageId = message.messageId,
};
Bus.Reply(response);
}
My question is as follows. When a request is sent to the transformationHandler. A message is sent to the messagequeue. Then hypothetical, the server crashes. The server reboots, the server picks up the TransformationRequest, does it work and wants to do a reply to the Saga, but how? The saga is not alive anymore and can't handle the .Register. How do I handle this problem?
Thank you.
NServiceBus will find the saga instance using a header in the response message automatically for you.
As Sean mentions you need to skip .Register and add a handler for transform result in your saga. The reason is that callbacks using .Register is stored in memory and therefor won't survive a restart

Resources