Dispatching to redux store from outside of a saga - reactjs

I have a saga that initialize an analytics provider, which lives completely outside of the redux context. However periodically Redux will push an updated auth token to this analytics provider.
(Yes i know the analytics code should probably live in the saga flow, but trust me I considered it and that refactor is not possible right now)
function* setupAnalyticsProvider(response: any): any {
// get some global session data
setupAnalytics(data)
}
export function* refreshTokenIfNecessary() {
// syncs new JWT token with redux state
updateAnaltyicsProviderWithNewToke(token)
}
The problem is I want the analytics code to periodically request a token refresh. In order to do this I want to pass in a callback that lets the Analytics code dispatch an action to trigger the refreshTokenIfNesscary() saga.
function* setupAnalyticsProvider(response: any): any {
// get some global session data
setupAnalytics(data)
setAnaltyicsRefreshCallback(() => {
// Dispatch action
})
}
Is there anyway to hook into the store dispatch method, or maybe using Saga-Channels to achieve this?

For any periodic execution of your analytic code ,you would have to create a callback for it to flow ,if the refactor is the problem use Event channel sockets for emit ,on and yield your callbacks.

Related

Why do I need redux async thunks

I'm new to whole react redux world but I would like to think that I now know how redux works. However right now I'm facing a new challange I need to implement async data fetching. I've choose to use axios as my http client.
The problem I have right now is that I've read about redux async thunk but I have no idea when and why would I use it. I understand that it adds middleware to redux which can handle async/await and promises.
I want to simply use axios to get data and then use dispatch to store them. Like this
const loadData = async () => {
const res = await axios.get('https://www.api.com/mydata');
const data = res.data;
dispatch(setMyData(data));
}
How would createAsyncThunk help me with that?
In your example, loadData only waits for one api request and then dispatches one simple redux action (setMyData). If you need it exactly like this, you're correct, why would you need thunks?
But imagine the following:
Several components in your app need to be aware that this api request is in progress (for example to show a loading indicator or to hide a button)
This function is needed in more than one place
You need to deal with specific error responses to the api request
Something in the global redux state could have changed while waiting for the api request to finish. You need to react to this before dispatching setMyData().
All of these are common requirements for complex react/redux apps, you might not have them right now but are likely to run into them at some point.
The thunk middleware provides an abstraction that deals with them. You could achieve the same by writing your own helper functions etc. but you'd reinvent the wheel in the end and a lot of react/redux devs would end up writing the exact same boilerplate code.
By design, redux actions are meant to be synchronous. Adding thunks like redux-thunk allow you to write action creators that are async and therefore return promises.
For example, I can write an action creator that looks like this:
const getUsers = () => async dispatch => {
let users = await getUsers();
dispatch({
type: GET_USERS,
payload: users
});
}
So instead of calling an api, fetching data, and then dispatching an action, you can have an action that does the fetching inside it

How to wait for asynchronous operation to complete before rendering every time state changes

I'm working on an app that displays images that are dynamically generated by the backend in response to changes in the current state.
I only want a component to re-render after a state change after the asynchronous call to the backend has returned with the new image.
Then I want to atomically update the image (in a canvas, say) and other UI based on the state - e.g. some HTML.
If I was generating a new image synchronously on the client I could use a (Layout) Effect hook to update the canvas.
I could only change the state after the network request to the backend returns - but I'm not sure of the cleanest way of doing this. I have to copy a whole bunch of state (into a "pending" state) and then mutate a bit of it, send it to server and only apply it when the call returns? It could get a little complicated if there are multiple state changes in-flight at once. Then I need to continue to mutate "pending" state in response to subsequent actions.
I wonder - is there something in React (or Redux?) that can help with this? Or should I try code it up myself?
BTW I'm new to React.
You're basically looking for async actions you can learn to do it natively here or use a middleware like redux-thunk (or redux-saga but its a bit complicated to be honest).
The basics are for every async requests you need to make 3 actions
A action to start the request.
A action when the action completes.
A action if the request fails.
and the code will look something like
function async makeRequest() {
dispatch({type: 'START'})
try {
const res = await myRequest()
dispatch({type: 'SUCCESS', payload: res})
} catch (e) {
dispatch({type: 'FAILURE', payload: e})
}
}

React / Redux - async action is working without using thunk

I recently tried something in my project and I was quite surprised that it worked. What I did was this :-
let result = {};
Axios.post("/auth", newUser).then(res => {
// console.log(res);
result = res.data;
this.props.signupUser(result);
});
The above code was written inside onSubmit event handler of a form in react. I wanted to get the response from the POST request and store the response in redux store and I tried the above code and it worked. The result was stored in redux store. So my question is was this supposed to work? and if yes then what's the purpose of redux thunk?
signupUser is a simple action creator, a function which returns a plain object and the variable newUser is an object which contains the form data.
The purpose of thunk is to give you more control over the async actions. While your code works, consider some more advanced requirements:
A different React component is supposed to show a loading animation while the request is in progress
You you want to track all api error responses somewhere
after the user has signed up, you want to trigger other requests for unrelated resources
Thunk facilitates all of those things in a more convenient way. You can also look at the current redux state at any point with getState in thunk actions. In general, it's better to separate concerns: React components show a signup form and are wired up with redux actions. The actions take care of api requests and data handling. Nothing is stopping you from writing all that code inside of a React component, but in a larger app this will get out of hand quickly.

in react-redux and redux-saga what's a proper method of transitioning to a new route after an async call succeeds?

i'm using react, redux, and redux-saga, and react-router-redux. i'm looking for a 'right' way to transition to a new route after a successful async call.
for example, a user submits an order form and is redirected to a thanks page. behind the scenes, we
current route is orders/new
dispatch(submitOrder())
submitOrder() saga runs, async network call
api returns success, SUBMIT_ORDER_SUCCESS action is dispatched
at this point i want to transition to orders/:orderId/complete.
where/how is the right way to do this?
using OrderFormContainer.componentWillReceiveProps() hook is possible, checking a bool like nextProps.submitDidSucceed and then calling a transition action - but that's brittle and feels fundamentally wrong.
You can use push action creator inside your saga to navigate to a new location:
import { push } from 'react-router-redux';
//... your saga code
// api return success
yield put(SUBMIT_ORDER_SUCCESS); // your existing action
yield put(push('/orders/:orderId/complete')); // use push to redirect to desired location

Loading Initial Data from a web service call

I am reading this tutorial on writing Async Actions
http://redux.js.org/docs/advanced/AsyncActions.html
and based on this, I have a method called fetchPosts in my action. this method first dispatches a requestPosts action, then it calls the web service and finally dispatches receivePosts with the results.
My question is from where should I call this method so that when my application loads, the data is loaded from web service?
Here is some options depending from app logic
1) If you want access to posts globally, across all components
you can put fetchPosts right after creating store, example from
redux/examples/shopping-cart
const store = createStore(
reducer,
applyMiddleware(...middleware)
)
store.dispatch(getAllProducts())
2) If you want access to posts only for specific component/page
you can put fetchPosts call in componentWillMount method of specific component.
componentWillMount() {
loadData()
}
Some examples from redux official examples
redux/examples/real-world/containers/RepoPage.js
redux/examples/async/containers/App.js
redux/examples/real-world/containers/UserPage.js

Resources