How/Where can I dispatch actions periodically? Using recursive setTimeout to make a countdown.
Taken from the example, something similar to this:
// Can also be async if you return a function
export function incrementAsync() {
return dispatch => {
(function _r() {
setTimeout(() => {
// Yay! Can invoke sync or async actions with `dispatch`
dispatch(increment());
_r();
}, 1000);
})();
};
}
So is this a good idea, or there is a better approach to this problem, like using middlewares or creating actions from somewhere else?
I prefer a generic version of this, where I can control start/stop of the timer via the store.
I've setup a sample implementation please take a look at https://gist.github.com/eguneys/7023a114558b92fdd25e
The approach you suggest is fine, although a bit convoluted. In general, I'd set intervals inside component lifecycle methods (e.g. componentDidMount / componentWillUnmount) and avoid actions setting intervals on other actions.
If you absolutely need this flexibility, a better bet is to use Rx for async management, and dispatch the actions at the very end of observable chain. This way you use Redux where it shines (synchronous updates) and leave asynchronous composition to Rx.
Related
How can I await, to resolve/reject, the promise returned by a function wrapped in the react hook "useMemo"?
Currently, my code looks like this:
// Get the persisted email/username
const persistedUsername = useMemo(async () => {
let username;
try {
username = await AsyncStorage.getData(`#${ASYNC_STORAGE_KEY}:username`);
} catch {}
return username;
}, []);
EDIT
What I am trying to achieve is to get the data before the component is rendered, some kind of "componentWillMount" life-cycle. My two options were:
Computing the value using useMemo hook to avoid unnecessary recomputations.
Combine useEffect + useState. Not the best idea because useEffect runs after the component paints.
#DennisVash has proposed the solution to this problem in the comments:
Blocking all the visual effects using the useLayoutEffect hook (some kind of componentDidMount/componentDidUpdate) where the code runs immediately after the DOM has been updated, but before the browser has had a chance to "paint" those changes.
As you can see, persistedUsername is still a promise (I am not waiting the result of the asynchronous function)...
Any ideas? Is it not a good idea to perform asynchronous jobs with this hook? Any custom hook?
Also, what are the disadvantages of performing this operation in this way compared to using useEffect and useState?
Same thing with useEffect and useState:
useEffect(() => {
// Get the persisted email/username
(async () => {
const persistedUsername = await AsyncStorage.getData(
`#${ASYNC_STORAGE_KEY}:username`
);
emailOrUsernameInput.current.setText(persistedUsername);
})();
}, []);
Thank you.
Seems like the question is about how to use componentWillMount with hooks which is almost equivalent to useLayoutEffect (since componentWillMount is deprecated).
For more additional info, you should NOT resolve async action in useMemo since you will block the thread (and JS is single-threaded).
Meaning, you will wait until the promise will be resolved, and then it will continue with computing the component.
On other hand, async hooks like useEffect is a better option since it won't block it.
I am confused right now. Learning js frontend and how to make API requests.
Till now, whenever I made an API call, I always used something, that let me handle an asynchronous request (axios or thunk).
But now I got a case, where I am making a request to my Firebase/Firestore and in the yt video, that I watched, only useEffect was used. Without any async/await, like:
useEffect(() => {
// snapshot - to check, if there is a change (like a new entry) in the database
db.collection('products').onSnapshot((snapshot) => {
setProducts(snapshot.docs.map((doc) => doc.data()));
});
}, []);
Why is this?
In the example code snippet
useEffect(() => {
// snapshot - to check, if there is a change (like a new entry) in the database
db.collection("products").onSnapshot(snapshot => {
setProducts(snapshot.docs.map(doc => doc.data()))
})
}, []);
db.collection("products") manages its own asynchronous event handling. When a "snapshot" event is received it invokes the onSnapshot callback and updates state. There is nothing to await on, especially since there is no value returned.
You should also note that useEffect hook callbacks are 100% synchronous code, they can't be marked async and await any code calls. They can, however, call async functions that do their own awaiting and such.
About the questions in your title
Use a useEffect hook any time you want to issue any side-effect from the component lifecycle, like console logging state, make data fetches, dispatching actions. This is mostly due to the fact that the entire function body of a functional react component is to be considered a pure function. useEffect with dependencies is more akin to a class-based component's componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods. Alone they are just called as part of the react component lifecycle.
Use axios, or fetch, or any other data fetching code whenever you need to fetch that data.
useEffect() - It is a hook that allows us to perform side effects in functional components like network request etc.
Axios - It is just a library where we can make http request like fetch API in javascript and also axios is promise based.
I have a callback which I need to return true or false to trigger some external action, I need to make an API call inside this callback, so I need to get the state and dispatch some actions inside the callback, I don't know if I can use eventChannel because this callback could not be a generator, only a plain function. I need to do something like this.
zafClient.on('ticket.save', () => {
const state = yield select();
yield put(actionWithApiCall())
// I need to wait for the action to finish and then return something
// based on the response from the API
// I know how to block the saga to wait for the action dispatched
// the problem is that I can't use yield here
return somethingfromthestore;
});
Btw, this is zendesk API.
Your not going to be able to pass a generator function to that API. The work around is to dispatch an action directly to the redux store and then write a saga that listens for that action.
zafClient.on('ticket.save', () => reduxStore.dispatch(actionWithApiCall()))
You will have to make the redux store exportable from where you create it. So that you can directly access it here.
One of the challenges is how to yield in the callback. How about importing dispatch? Isn't dispatch the non-generator version of yield?
Should you be using React, doing so seems to be an option.
In the same saga that you use to specify the callback, you can subsequently listen for the action created in the dispatch/action creator callback. That's done by specifying another watchFor function (in my setup, any number of exported watchFor functions get added to the pool of saga watchers).
The paired saga worker can finalize whatever needs to happen to the data returned from the API call before using your final action creator to "document" the workflow by updating the store.
The other option might be to wrap the api call in an eventChannel (see: https://github.com/redux-saga/redux-saga/issues/1178).
- E
How do I know when an Observable producer is async or sync?
An sync example:
Observable.of([1, 2, 3])
another async example (ngrx Store, see here)
this.store.take(1);
And now an obvious async example:
this.http.get(restUrl)
I fully understand how this works, and that some Observables can be sync and others Async. What I don't understand is how i can tell the difference in advance? Is there for example an obvious interface on the producer that tells me that it will be async?
tl;dr
The main reason this question has come up is because of the answer from the link above (here).
#Sasxa has (correctly) answered that we can use a synchronous call to get the latest value from the ngrx store:
function getState(store: Store<State>): State {
let state: State;
store.take(1).subscribe(s => state = s);
return state;
}
However knowing that observables are usually asynchronous, I saw this and immediately thought RACE CONDITION! the method could return an undefined value before subscribe has called back the function.
I've debugged through the internals of Store and Observable and this example is indeed synchronous, but how should I have known that? For those that know ngrx, the select method of store (used to get the latest values) is asynchronous, without a doubt, as this is what gives us the reactive gui, which is why I came to the assumption I did.
It means that I cannot refactor the code above as follows:
function getLatest(observable: Observable): any {
let obj: any;
observable.take(1).subscribe(latest => obj = latest);
return obj;
}
I could call this with any Observable and it would work for synchronous producers - it may even work SOME OF THE TIME for async producers, but this is without a doubt a race condition if an async observable is passed in.
It is possible to determine if an observable is asynchronous for sure if was directly scheduled with asynchronous scheduler (Scheduler.async or Scheduler.asap), it's exposed as foo$.scheduler:
let schedulers = [null, Scheduler.asap];
let randomScheduler = schedulers[~~(Math.random()*2)]
let foo$ = Observable.of('foo', randomScheduler);
This information becomes even less available when foo$ is processed further, e.g. chained with other operators.
And it's impossible to determine if values will be produced synchronously (on same tick) or asynchronously because this depends on observable internals:
let foo$ = new Observable(observer => {
if (~~(Math.random()*2))
setTimeout(() => observer.next('foo'));
else
observer.next('foo');
});
TL;DR: it's impossible to know this for sure.
It is hard to figure out whether an observable you get is sync or async (imagine you get observables return from a thrid party library). That's why you write it in a fashion that the execution order is controlled and predictable.
That's also why there are operators like concat, combineLatest, forkjoin, switchMap, race, merge to help you get the order and performance right for different scenario
I'm learning redux saga but don't get what the author means by the below:
Contrary to redux thunk, you don't end up in callback hell, you can
test your asynchronous flows easily and your actions stay pure.
Any examples of "callback hell" and "pure actions" would be much appreciated.
Pure Functions
Pure functions are a type of function that, when given the same arguments, will always produce the same result, and doesn't modify any variables that are outside of its scope. For example,
function addTwoIntegers(a, b) {
return a + b;
}
If we pass a=2 and b=3, then the result will always be 5. Nothing else is changed. This is a good thing, because it makes testing that function much easier - we know with certainty what they output will be for a given pair of inputs.
Contrast this with, for example, a function that loads data from an API and assigns it to a variable:
let result;
function AddIntegerToNumberFromServer(a) {
fetch('api/numberGenerator').then(function(res) {
result = a + res.data.number;
});
}
In this case, we can call the function with the same argument multiple times, and not be certain of getting the same result. The consequence of this is that the function is much harder to test - we probably have to create a mock of the api. We also have to be certain what the initial value of result was in order to know if our function worked as planned.
Callback Hell
As you probably know, callbacks are functions that we pass as arguments to another function, so that we can defer execution of some code until the second function has completed. Callback hell occurs when we get many layers deep into this - as commented by someone above jQuery seems to invite this style - for example:
$(function() {
$('#element').click(function() {
$(this).fadeOut(function() {
$('#anotherElement').fadeIn();
}
}
}
The issue here is primarily one of human understanding of the code, and ease of readability. Imagine if this went more layers deep (not uncommon) how difficult it could be to work out what is happening and when.
Redux-thunk is far from the worst offender when it comes to callbacks, and the readability factor can be mitigated by using promises instead of callbacks.
... your actions stay pure ...
My best guess is that this is a mistake. In the redux-thunk README there is no action creator that is impure let alone an action object that is somehow impure. Perhaps "impure actions" is referring to a pattern by which "higher level" action-creators like makeSandwichesForEverybody contain multiple side effects and are therefore very hard to test.
... end up in callback hell ...
Again makeSandwichesForEverybody contains many nested promises which might become harder to fathom as the async work flow becomes more complex. What I can't yet comment on is how much simpler / complex this would be implemented in redux-saga.
function makeSandwichesForEverybody() {
return function (dispatch, getState) {
if (!getState().sandwiches.isShopOpen) {
// You don’t have to return Promises, but it’s a handy convention
// so the caller can always call .then() on async dispatch result.
return Promise.resolve();
}
// We can dispatch both plain object actions and other thunks,
// which lets us compose the asynchronous actions in a single flow.
return dispatch(
makeASandwichWithSecretSauce('My Grandma')
).then(() =>
Promise.all([
dispatch(makeASandwichWithSecretSauce('Me')),
dispatch(makeASandwichWithSecretSauce('My wife'))
])
).then(() =>
dispatch(makeASandwichWithSecretSauce('Our kids'))
).then(() =>
dispatch(getState().myMoney > 42 ?
withdrawMoney(42) :
apologize('Me', 'The Sandwich Shop')
)
);
};
}
As an aside I have used "impure action creators", which I call dispatchers in the form of functions that acts as action creators, dispatcher and has side effects. I have found this to be a good pattern for managing async work. However it makes isomorphic apps harder / impossible to implement and is not easy to test.