Loading Initial Data from a web service call - reactjs

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

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

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.

Dispatching to redux store from outside of a saga

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.

ComponentDidMount and dispatching an action

I do have a React component that loads when user clicks on a ReactRouter Link such as http://mysite/page/:id.
Inside the ComponentDidMount method I'm using the 2 following actions:
postsSetPage(page)
should be a SYNC action that changes the page right away in the store).
I'm passing it the id given from ReactRouter as page.
postsFetch(args):
is an ASYNC action that fetches data from API.
So my code looks like this:
componentDidMount() {
this.props.postsSetPage(this.props.match.params.id);
console.log(this.props.posts.query); // <========== this will not be updated
this.props.postsFetch(this.props.posts.query);
}
this.props.posts.query contains all arguments to apply filter/ordering/pagination on my API. It looks like this:
this.props.posts == {
records: [],
query: {
page: 1,
arg1: "asdasd",
argN: "blabla",
},
}
THE QUESTION:
The code inside componentDidMount doesn't work because after I set the page this.props.posts.query hasn't updated yet (console.log is confirming) and when I call fetch action query doesn't contain the correct page.
So aren't actions such as setPage suppose to be sync?
All help will be greatly appreciated!
setPage as a redux action is synchronous, but the resulting setState which happens due to change in redux store might not be (it's better to assume setState is never synchronous).
And if your setPage action goes through a middleware which does some async stuff (fetching data from api) and then forwarding the action, then the action itself wont' lead to setState call synchronously, because redux store would only change once the api data comes back.
But i guess you are facing the first problem.
One way to take care of your problem can be to do the fetch in either the action (thunked), or do it in a middleware.
As per React, this.props are the immutable object means you can not update it programmatically.
Here you will have to use this.state

proper react component usage

I have a project that uses React and Redux. I have multiple React components that need to use a common method that will scrape data out of the Redux Store, build a request object and fire off a rest request to the server.
I wouldn't want to write the method that does this more than one time. So, should this method be it's own React component, or should I just put it in a common javascript file? I'm not sure what the point of having a component would be if you're not rendering any JSX. On the other hand, if I put it in a common javascript file, is it possible to wire up redux to that file to get access to the states in the store?
Thanks in advance.
Adding the redux-thunk middleware lets you dispatch functions, which then get access to dispatch and getState(). From there you can do anything you want, such as using selector functions to extract pieces of state that you need, and making AJAX calls. The thunk action creators can be passed to components as props, including binding them to auto-dispatch:
function someThunk() {
return (dispatch, getState) => {
const state = getState();
// do anything you want here
}
}
const actions = {doStuffOnClick : someThunk};
export default connect(mapState, actions)(MyComponent);
Further resources:
redux-thunk is described in its repo at https://github.com/gaearon/redux-thunk .
I have a gist demonstrating some common thunk usage patterns at https://gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e099f857e.
My React/Redux links list has links to articles discussing thunks and side effects in the Redux Side Effects category.
It should not be a Component because, as you said, there is no visual aspect to it.
Create a new action that does just that and dispatch the action from anywhere you need. This action could check the store to see if the action has already happened and then do nothing (or refresh).
The module that uses import {createStore} from 'redux' creates your store. Import the created store in the action module and use store.getState() to inspect the current state of your store.

Resources