Why use immer for this function? - immer.js

ToggleAttached function inside CauseSpecificPage.js. This function toggles the follow/following button on the cause specific page: st-bnv.
What is the benefit of using Immer in this situation? Do we even need to use Immer?
const ToggleAttached = () => {
if (state.isDisabled) {
return
}
const oldValue = state.isAttached
setState(produce((draftState) => {
draftState.isDisabled = true
draftState.isAttached = !oldValue
}))
ToggleFollow({ causeId })
.then(response => {
setState(produce((draftState) => {
draftState.isAttached = response.data.isAttached
}))
})
.catch(error => {
setState(produce((draftState) => {
draftState.isAttached = oldValue
}))
HandleError(error)
})
.finally(() => setState(produce((draftState) => {
draftState.isDisabled = false
})))}

Immer is a little more verbose but is more maintainable in the long run. Immer was created to help us to have an immutable state, it’s a library created based on the “copy-on-write” mechanism — a technique used to implement a copy operation in on modifiable resources.
We can see that the concept of immutability is getting used more and becoming more common in the React community. But to make sure that we’re doing it the right way, we can use Immer for the job. Immer adds more benefits on redux states.

Related

Why is my React state hook (setState) so picky about the array I give it?

There is something about state hooks in React that I don't understand. I am dealing with the following situation in my code:
const [myArray, setMyArray] = useState(["foo"])
useEffect(() => {
console.log("myArray has been updated")
}, [myArray])
const clickUpdate = () => {
var myUpdatedArray = myArray
myUpdatedArray.push("bar")
setMyArray(myUpdatedArray)
}
This does not work. Calling the clickUpdate does not update myArray.
The problem is in the clickUpdate function. When I rewrite the function in the following way it works just fine:
...
const clickUpdate = () => {
var myUpdatedArray = [...myArray, "bar"]
setMyArray(myUpdatedArray)
}
or alternatively:
...
const clickUpdate = () => {
setMyArray(myArray=> [...myArray, "bar])
}
Either solution works for me just fine, but I am curious:
What is it about the .push mutation of myUpdatedArray that makes my setMyArray not work properly? Or what else is going on here that I am missing?
Thank you!
React compares the values in the deps array by reference, that's way it works when you create a new array.
If you are just going to add/remove items (not editing them) then you can be more performant and pass myArray.length to the deps list. Thus react will detect changes when the length is changed.
useEffect(() => {
console.log("myArray has been updated")
}, [myArray.length])

Can a series of promises and then handlers be written in waitOneCycle.then(waitOneCycle().then(...)) but without deep nesting?

If we already have
const waitOneCycle = () => {
return new Promise(resolve => setTimeout(resolve));
};
Then our code (this is currently used in Jest and SinonJS although it doesn't have to be:)
waitOneCycle()
.then(() => {
// do something
});
and it reads really elegantly: the waitOneCycle and then do something.
However, if we do a series of them, we have to:
waitOneCycle()
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
})
.then(() => {
// do something
return waitOneCycle();
});
and it reads in a strange way, because why would we return something to act as "waitOneCycle"? This is the way it works, but the code just read in a strange way.
We could do something like:
waitOneCycle()
.then(() => {
// not return anything here
})
.then(() => {
waitOneCycle()
.then(() => {
});
})
but this would go back to the nesting hell issue in the past. Is there a way to refactor it so it reads coherently but at the same time all actions are serialized?
P.S. in the case of Ruby, if the promises are chained this way, it actually can read out quite well, as we don't need to use return for the last value evaluated. So it'd read like waitOneCycle, then, waitOneCycle, then... and it appears quite elegantly.
You don't need to nest (and it would only work if you were to return the inner promise anyway), you can also chain it:
waitOneCycle()
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle)
.then(() => {
// do something
})
.then(waitOneCycle);
This might work even better if you make waitOneCycle pass through the value:
const waitOneCycle = (v) => {
return new Promise(resolve => setTimeout(resolve, v));
};
Of course, if you want promise code that reads coherently and has no nesting (not even that of then callbacks), you should just go for async/await syntax.

React Redux thunk - Chaining dispatches

Currently i'm building an application that is heavily dependant on API calls. The api calls are done within Redux actions with Thunk middleware like so:
export const brand_fetchAll = () => {
return dispatch => {
fetch(apiURL+'brand')
.then(response => {return response.json();})
.then(content => {
dispatch({
type: 'BRAND_STORE_ALL',
content
})
})
.catch(error => console.log(error))
}}
In my component, i'm first fetching the data through separate actions. After that i'm opening up an editor:
// A component method
editModeOn(){
// Fetch data
this.props.dispatch(campaign_fetchAll());
this.props.dispatch(brand_fetchAll());
// Open editor
this.props.dispatch(page_editModeOn());
}
Right now the editor opens before the api calls have completed, so no data is being shown. It's possible to chain the dispatches within the actions, but i want to keep the modularity, so i don't have to create hundreds of custom API calls. Ideally what i want is to chain them using something like promises:
// A component method
editModeOn(){
this.props.dispatch(campaign_fetchAll().then(brand_fetchAll()).then(page_editModeOn());
}
Unfortunately i didn't yet get that to work. I hope someone can help me out. If you need more information i'm happy to hand it over. Better ideas are also very welcome :)
Thanks in advance!
Would a callback function be an option for you?
So update your code to be;
export const brand_fetchAll = (callback) => {
return dispatch => {
fetch(apiURL+'brand')
.then(response => {return response.json();})
.then(content => {
dispatch({
type: 'BRAND_STORE_ALL',
content
});
callback();
})
.catch(error => console.log(error))
}}
// A component method
editModeOn(){
// Fetch data
this.props.dispatch(campaign_fetchAll());
this.props.dispatch(brand_fetchAll(() => {
// Open editor
this.props.dispatch(page_editModeOn());
}));
}
You are chaining the callback onto the end of the api call success, however, you are not tightly coupling what it is as you are passing this in depending on the usage.

Is there a better way to form this code example?

I'm new to rxjs and using redux-observable. The short of it is that I need to make a couple promise requests when i get a connection then output the results. I'm wondering if there is a way to join this into a single map at the end and not have to call store.dispatch multiple times and have the retry work for each individual read. Thanks ahead of time for your comments.
export const handleBleConnectionSuccess = (action$,store,{bleCommunicator}) =>
action$.ofType(c.BLE_CONNECTION_SUCCESS)
.do((a)=>{
Observable.fromPromise(bleCommunicator.readCharacteristic(a.device.id,gattInfo.uuid,gattInfo.firmwareRevision.uuid))
.do((value)=>store.dispatch({type:c.DEVICE_FIRMWARE_VERSION,device:{...a.device,firmwareVersion:value}}))
.retry(3);
Observable.fromPromise(bleCommunicator.readCharacteristic(a.device.id,gattInfo.uuid,gattInfo.modelNumber.uuid))
.do(value=>store.dispatch({type:c.DEVICE_MODEL_NUMBER,device:{...a.device,modelNumber:value}}))
.retry(3);
})
.mapTo({type:'DEVICE_INFORMATION_REQUESTED'});
I'm wondering if there is a way to join this into a single map at the end and not have to call store.dispatch multiple times and have the retry work for each individual read
Yes, there is a better way, and it's possible to do what you want.
From the syntax, I'm guessing that you use ngrx (effects) (and not redux-observable).
So with ngrx/effects you could do it like that:
export const handleBleConnectionSuccess = (
action$,
store,
{ bleCommunicator }
) =>
action$.ofType(c.BLE_CONNECTION_SUCCESS).switchMap(a => {
const readCharacteristic = deviceOrFirmwareUuid =>
bleCommunicator.readCharacteristic(a.device.id, gattInfo.uuid, deviceOrFirmwareUuid);
return Observable.merge(
readCharacteristic(gattInfo.firmwareRevision.uuid)
.map(value => ({
type: c.DEVICE_FIRMWARE_VERSION,
device: { ...a.device, firmwareVersion: value },
}))
.retry(3),
readCharacteristic(gattInfo.modelNumber.uuid)
.map(value => ({
type: c.DEVICE_MODEL_NUMBER,
device: { ...a.device, modelNumber: value },
}))
.retry(3),
{ type: 'DEVICE_INFORMATION_REQUESTED' }
);
});

Is It good to subscribe to an observable within another subscription handler?

I'm using Angular2 and I have a question about what is the best way to do if I have many observables.
Can I put subscriptions inside each other or put each one in a different method and put the results in class properties?
Example :
ngOnInit() {
this.route.params**.subscribe**(params => {
if (params['id']) {
this.load = true;
this.batchService.getPagesOfCurrentObject(params['id'], "10", "0")
**.subscribe**(result => {
this.stream = result;
if (this.stream.length > 0) {
this.stream.forEach(page => { this.batchService.getPageStreamById
(page.pageId)**.subscribe**(pageStream => {
let base64 = btoa(new Uint8Array(pageStream.data)
.reduce((data, byte)
=> data + String.fromCharCode(byte), ''));
this.pages.push(base64 );
})
return;
});
}
},
error => this.errorService.setError(<any>error),
() => this.load = false
);
}
});
try {
this.customer = this.sharedService.processSelect.subscription.customer;
} catch (err) {
return;
}
}
Having multiple observables is totally fine, this is what reactive programming is about :)
But here your problem is having too much subscribe. Keep in mind that subscribe is a way to create side effect. To have an easy to read code, you should try to use the least possible subscribe.
Your use case is the perfect use case for the mergeMap operator, that allows you to flatten nested observables.
Here what your code would look like
const response$ = this.route.params
.mergeMap(params => {
return this.batchService.getPagesOfCurrentObject(params['id'])
})
.mergeMap(stream => {
return Rx.Observable.merge(stream.map(page => this.batchService.getPageStreamById(page.pageId))
})
.map(pageStream => /* do your stuff with pageStream, base64 ... */)
response$.subscribe(pageStreamData => pages.push(pageStreamData))
See how there is a single subscription that triggers the side-effect that will modify your app's state
Note that I voluntarily simplified the code (removed error handling and checks) for you to get the idea of how to do that.
I hope it will help you thinking in reactive programming :)

Resources