Getting callbacks from redux-saga - javascript

I am triggering a function in saga with Dispatch. Can Saga send me information inside the component after it's done all its work? Actually, I will explain what I want to do with a simple example, don't laugh, please, I am sure this won't work.
dispatch({
type: 'triggerApiCallInSaga'
}).then(res => doSomething(res.payload));
I want to make changes in the component as a result of the query I have made. I actually want to maintain this with redux, but I'm worried about my state expanding. Also it seems silly to me to check via redux to close the modal.
In short I want to receive signal from saga and see it in component. Can I make a listener for it? Or does saga have a method for this? Although I searched, I could not find the right results. redux-sagas callback (aka sagas and setState) I encountered a similar question to my question, but here I encountered a negative answer that I did not understand.
Thank you.

The answer you linked is right, the expected way for a saga to communicate back to component is through updating redux store. Adding promises/callbacks is an antipattern, because each action can be handled by 0-n sagas - of course you might know in your case it is just one saga, but the framework doesn't guarantee that in any way and so it can lead to some unexpected issues later on.
That said, even though it is an antipattern doesn't mean you can't do it.
The easier way is to add an callback to the action, so e.g.
// component
const callback = data => console.log(data)
dispatch({type: MY_ACTION, callback})
// saga
yield takeEvery(MY_ACTION, function*(action) {
action.callback({foo: 'bar'});
})
If you would prefer promises, that is something that can't be done easily with just the saga library, but there are packages that can add this functionality like:
https://github.com/adobe/redux-saga-promise
Ideal solution to this problem would be to run a saga on top of a useReducer hook, so that instead of adding component specific state to redux store you would just work with a component state. There are some attempts out there that try to do this, but unfortunately react is still missing some functionality that would allow us to reliably do this without introducing other issues.

Related

Why React doesn't use promise.resolve for batching updates?

Currently, React only batches updates in functions that are called by the React itself. like event handlers and lifecycles. And as far as I know, React simply calls these functions like they are wrapped in unstable_batchedUpdates.
Lets assume it's something like the following:
unstable_batchedUpdates(() => {
instance.componentDidMount();
});
So right now, we have to manually wrap functions that are called by someone other than the React inside of the unstable_batchedUpdates.
I'm also aware that in the concurrent mode, we have the updates train concept and we batch all of the state updates together.
But I have two questions,
First, how does this update train works? What mechanism decides which updates should be on the train? Is it only the time of occurrence?
And second, Why it has to be this complicated? why we couldn't do something similar to what Vue does and wrap all of the setState calls inside of a nextTick function? I know it's almost similar to what we will have in the concurrent mode, but what kind of bottlenecks and problems do we have for adding it right now, without the concurrent mode enabled?
I'm thinking of starvation problem. But I'm not sure.
Thanks in advance.

Why does wrapping a redux call into a promise allows the access of the result "immediately"

So we have a redux (with thunk middleware) / react environment.
We have the following piece of code:
onMyClick = () => {
this.props.mySynchronousActionWhichWillCreateNewReducerState();
this.setState(...my state change which wants to see the new reducer state...);
}
It will not work in this form since the code is synchronous. Which means that the react lifecycle will never get to update itself with the new props.
However if we change it like this:
onMyClick = () => {
Promise.resolve(this.props.myActionWichWillCreateNewReducerState())
.then(
() => this.setState(...my state change which want to see the new reducer state...)
);
}
It now works as "expected" (The dispatch triggers the new reducer state, the component updates, then the setState runs). First I thought this solution is error prone and works because we 'win' just a bit of time with asynchronicity to allow the update to kick in before the setState. But it never fails, (You can make the code slow in the action, or in the reducer, in the middleware, in the component update, where ever you want, it still works).
Why?
Some explanation might be warranted why this is so hard to wrap my head around. And more in the sense of "Why does it work the way it does" instead of "How does it work"
So first and foremost lets look at the two pieces of code like plain javascript.
In this case - for me at least - the first should work, and the second should not. Or at least the second should be fuzzy.
First:
I make synchronous call (dispatch->action creation->store change), then I make an other, and yet the second cannot expect the changes made by the first. I have to know how redux and react operates quite intimately to know how, and why. And btw you can even mutate the redux store (big no no) instead of returning a new object from the reducer to retain the reference and it still doesn't work. Which is mind boggling, you synchronously mutate an object, then cannot access the change afterwards...
Second:
In this case (just like Jaromanda X commented) what I "seemingly" tell the code 'Hey run these two pieces of code in parallel'. And now it works, and works all the time. Wut. Adding my (superficial or so it seems) understanding of react life cycles to the mix makes it even more paradoxical. Since it means that even more logic - react lifecycle update - will have to 'outrun' the setState call for it to work.
If this wouldn't be redux/react environment with all the support and intelligence behind it, I would say this code behavior smells like all hell and it smells like black-magic and go-to :).
When you wrap a piece of code inside a promise, you are essentially delaying its execution by a minimum of 1 tick. For your code, that time was sufficient enough for reducer dispatch to complete its update. Hence when the code inside then was executed it got the updated value, as this.state is an object and even within a closure it always points to a memory reference which will be updated.
That said neither reducer update in redux or setState in React returns a promise. Your code is equivalent to :
Promise.resolve(console.log("dummy")).then(() => console.log("second"));
console.log("first")
first will always be printed before second as the promisified snippet is executed in the next tick of the event queue.
Your code is not error prone at the moment because React decided 1 tick was sufficient to update the state. But don't rely on that as for some other piece of code or in later versions of React, the time required for updates might change.

Redux vs custom hook

I learned react and Redux at the same time and went "all in" on Redux; basically all state is stored in Redux. And I followed the standard allIds, byId state shape pattern as detailed here.
My app is very data-centric, it talks to an API, and does alot of CRUD type actions - fetchAll, fetchById, add, update, delete.
The API communication is segregated into a "service layer" module that is its own npm package. All calls to this service layer are in the Redux actions, using redux-thunk.
I've realized there is no need to put most everything in Redux, the data is really needed on a specific component, for example. And I would love to simplify this architecture.
So I began to refactor into a custom hook instead. It seemed since my state shape was more of an object rather than scalar, I should use useReducer rather than useState...
// reducer
// -------------------------
const initialState = {
adding: false,
updating: false,
deleting: false,
error: null,
items: null
};
const reducer = (state, action) => {
// implementation omitted for brevity. . .
}
const useItemsApi = () => {
const [state, dispatch] = useReducer(reducer, initialState);
// wrapped in useCallback because called in component's useEffect
const fetchItems = useCallback(async (options) => {
try {
const resp = apiService.fetchItems(options);
} catch (err) {
if(err.status === 401)
// send to login screen
else
dispatch({type: 'error', payload: err});
}
}, [options]);
// addItem, updateItem, deleteItem, etc...
const actions = {fetchItems, updateItem, addItem, deleteItem};
return [state, actions];
};
// component
// -------------------------
const component = (props) => {
const [state, actions] = useItemsApi();
const {fetchItems, updateItem, addItem, deleteItem} = actions;
useEffect(() => {
fetchItems()
}, fetchItems);
// omitted for brevity...
}
When I got to setting the state in the reducer for the update action, I realized it would be easier if I used "allIds" and "byId" pattern.
And at this point I thought - how is this any different than using Redux?
It is going to end up looking like almost the exact same code, and I'm losing some power of selectors, but removing the complexity of redux-thunks. And my current redux actions include specific use case actions (special save for item type X, for ex.) so I'd need to find a place for those.
My question is - is there any reason to refactor this to a hook using local state?
It really boils to three things for me:
Whether you need Redux's middleware, logging features etc or not
How much you care about future stability
Personal taste
Redux offers more than merely state management.
Offloading context handling to Redux is a big plus for me looking forward into the future.
If your application is very data-centric, I would not omit redux-devtools and other middleware (I personally like redux-observable).
When your app grows in complexity you will want to find out about corrupt state updates and state that gets triggered multiple times unexpectedly.
But then again, only you can assess the complexity of your own app and where it will be headed towards in the future.
The rest of this post is 'personal taste', but I'll add it.
Personally I like using Redux for a few different reasons than before mentioned.
I used Redux without using React at all not even so long ago, and also with a framework which nobody probably heard about, which is the Lightning web components framework from Salesforce.
The point being that it keeps state management and view logic in separate libraries.
React becoming a Swiss army knife is something I'm not really in favour of, personally.
React's core strength for me was that it was a view library featuring the virtual DOM with a clear purpose whereas now... well where is the border between it just becoming an opinionated framework?
Using React hooks is not imposed, but then again it sort of is.
If you use React, you will use all of it, this question bringing tribute to this conclusion.
And at this point I thought - how is this any different than using Redux?
So you refactor Redux to the useReducer hook and then wonder, why did I need this?
If you ask then you probably didn't.
Maybe that is just the answer to your question.
Reducer functionality just moved from a state management library to a view library (or is it?). Cool (I guess).
Advantages of storing the state in Redux:
You can access and modify it globally
It persists even after your component is unmounted
Advantages of storing the state in the component:
You can have multiple components with different values in the state, which may be something you want
...Or you could even have multiple hooks of the same type in one component!
You don't need to switch between files. Depending on how your code is organized, Redux can be split into 3 files + 1 file for the component which uses it - while this can help keep your code well-structured for complex use cases, it can be an overkill for keeping track of a simple state. Having to switch between multiple files to work on one component can reduce your productivity (I don't like having to keep track of 4 tabs in my IDE for every feature I work on).
(Also, hooks are new and cool.)
So, use Redux if:
You need to share state between multiple components (or plan to in the future)
You need to keep state even when the component that uses it is unmounted
You might prefer to keep the state in React (hooks or otherwise) in other cases since they simplify your code a bit.
But that doesn't mean you need to refactor your entire codebase. If you think your code is concise enough and you like the way it is organized, or if you are unsure if you will need the state globally in the future, you can keep it in Redux - there is nothing wrong with that!
No, you don't have to.
From my point of view, if only as a state management lib, Redux can be replaced by Hooks (useReducers ect) + local/shared state.
Redux comes before hooks and our app has been implemented by Redux, definitely you don't have the need to replace it.
We can plan to use hooks + shared state as an alternative in our new projects.
I did met with three situations, which are:
Redux ONLY;
Hooks ONLY;
Redux + Hooks;
All of them worked fine.
It's the dilemma that we're in the transition of Redux and Hooks. Hooks are made in a way not to replace redux but it can if you want to.
So in future, you can use any of those, depending on you use case.
Hope it helps.

Render after componentWillReceiveProps and before componentDidUpdate

Here's my problem:
I'm new at reactjs and I'm trying to make this app using SWAPI (swapi.co). For now I need to list characters and some info about them. The problem is I have this component called SelectedCharacter that returns some info about a character that was selected in a div.
The moment a character is passed through props to this component, I get a response via xmlhttp and the info is displayed. The thing is that I want to put a "Loading..." message while the data is fetched. This is how I was trying to figure it out:
I set up the componentWillReceiveProps function, where I test if I'll need to load stuff and the componentDidUpdate, where I fetch the data from this api and update the status.
I know, from react life cycle, that a render is called between componentWillReceiveProps and componentDidUpdate, and it indeed is.
I expected, then, that if I did this:
render() {
if (criteria) {
return <div>Loading...</div>
}
}
The thing is: even if this criteria is true (I tested it using console.log()), the message doesn't show until the next re-render. Am I doing anything too wrong here? If it helps, my code is at github.com/piubellofelipe/StarWars, the problem is at the selected_characters.js, in the src paste.
Thanks
I've been looking at your code, trying to work this out for you and I don't have any concrete answers for you, but I've noticed a few things that may be making things a bit unpredictable.
1. Calling forceUpdate
componentWillReceiveProps(){
this.setState({loading:true})
this.forceUpdate();
}
Calling setState will trigger a render, so the call the forceUpdate is not required here. This means there are more renders occurring than you might expect.
I believe this may be the cause of your issue for a pretty complicated reason. From the setState docs
... setState() is also asynchronous, and multiple calls during the same cycle may be batched together.
And from the forceUpdate docs
Calling forceUpdate() will cause render() to be called on the component...
My theory is that the call render triggered by setState, asynchronously setting loading to true, is being delayed and the one from forceUpdate is sneaking in first, while loading is still false.
2. Updating props
this.props.selected.moviesList = moviesListNames;
Components should never, ever, update their own props. Usually, this would be stored in state instead.
For more details on this, read this section of the docs and this answer.
3. Importing axios, but not using it
import axios from 'axios'
This one isn't really an issue (other than an unused import), and might just be preparation for where you're heading. axios gives a much nicer developer experience than XMLHttpRequest (in my opinion) for http requests and will clean up the fetchData function a lot, which will make troubleshooting easier.
I hope this helps a bit, and you're enjoying React. Feel free to follow up on any of these points in the comments or as new questions.

React Flux - dispatching within a dispatch - how to avoid?

I seem to have encountered a situation where I cannot avoid the dispatch-within-a-dispatch problem in Flux.
I've read a few similar questions regarding this problem but none of them seem to have a good solution besides setTimeout hacks, which I would like to avoid.
I'm actually using alt.js instead of Flux but I think the concepts are the same.
Scenario
Imagine a component that initially renders a login form. When a user logs in, this triggers an XHR that eventually responds with authentication information (eg. the user name), and then fetches some secure data based on the authentication information and renders it instead of the login form.
The problem I have is when I attempt to fire an action to fetch data based on the XHR response, it is still in the dispatch of the LOGIN_RESPONSE action, and triggers the dreaded
Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
Example
I have created this jsfiddle to demonstrate the problem.
I have a Wrapper component which either renders a login button or a Contents child component, based on whether the user is set in MyStore.
First, the login button is rendered in Wrapper component.
Clicking the button dispatches the LOGIN action.
After a delay, the LOGIN_RESPONSE action is dispatched (via the async mechanism in alt.js).
This action triggers MyStore to update the user name.
Wrapper component observes the store change and updates its state.
This causes Wrapper to render Content component instead of the login button.
Content component, on mount, attempts to dispatch the FETCH_DATA action, which fails because the dispatcher is still dispatching LOGIN_RESPONSE. (If I wrap the FETCH_DATA dispatch in a setTimeout it works, but it feels like a hack).
Variations of this seems to be a common scenario. In fact almost all the related questions have a similar scenario, but with no good or concrete answers.
React - Authentication process : Cannot dispatch in the middle of a dispatch
Dispatching cascading/dependent async requests in Flux/React
Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch
Is there something intrinsically wrong with this data flow? What is the proper Flux way of doing something like this?
This is a common problem with dispatching in componentDidMount in many libraries. The solution is to wrap dispatches in React's batched updates; luckily, Alt allows you to do this with the batchingFunction option:
var alt = new Alt({
// React.addons.batchedUpdates is deprecated:
// batchingFunction: React.addons.batchedUpdates
// use this instead in newer versions of React
// see https://discuss.reactjs.org/t/any-plan-for-reactdom-unstable-batchedupdates/1978
batchingFunction: ReactDOM.unstable_batchedUpdates
});
See https://jsfiddle.net/BinaryMuse/qftyfjgy/ for a working example and this Fluxxor issue for a description of the same problem in a different framework.
I believe our loyal friend, the Dispatcher, has its right to complain.
I will try to describe a hypothetical situation before throwing my conclusions. Let's say an app has two stores S1 and S2 and two kinds of actions A1 and A2. The right flow of an usual Flux implementation should be something like:
Component fires an action A1 (basically a dispatch);
Single dispatcher distribute correspondent payload to all registered stores;
S1 consumes the payload and maybe updates its state;
All components listening to changes in S1 check for changes they are interested in and maybe update their internal states (possibly triggering an re-render);
S2 consumes ... (like in step 3)
All components listening to changes in S2... (like in step 4)
Now all stores are done dealing with the action payload, components can fire new actions (A1 or A2).
One of the greatest advantages of using Flux over traditional MVC is that Flux gives you The Gift of Predictability. This feeling empowers the developer in such a way that they believe that, by correctly applying the Flux philosophy, they are sure that the order of execution is always somewhat similar to:
A1 > S1 > S2 > A2 > S1 > S2 > ...
This is a really great deal, especially when trying to find sources of bugs. One could argue that enforcing a predictable chain of events can lead to some inefficiency and he is probably right, especially when dealing with async calls, but that is the price you pay for having such a great power!
Due to the async calls, things can get a little messy. Something like the following chain of events could happen:
A1 > S1 > A2 > S2 > S1 > S2 > ...
Maybe your app can handle such chain of events quite well, but such "unpredictability" hurts basic motivations behind Flux's unidirectional data flow.
I feel there is no consensus in the community on how to handle such situations, but I will share my general solution: "for the sake of predictability, make sure to not trigger any new actions before you have the last one totally processed".
One way of doing this is by downloading all the necessary (new) data the app needs to re-render successfully before triggering any additional actions. In your example, this could be achieved by first downloading the data involved in the LOGIN_RESPONSE and FETCH_DATA actions and wrapping it in a single payload and then dispatch it, so all the components will have the data they want already in the stores without asking for more.

Categories

Resources