Data fetching with React Native + Redux not working - reactjs

I am building my first React Native app and use Redux for the data flow inside my app.
I want to load some data from my Parse backend and display it on a ListView. My only issues at the moment is that for some reason, the request that I create using fetch() for some reason isn't actually fired. I went through the documentation and examples in the Redux docs and also read this really nice blog post. They essentially do what I am trying to achieve, but I don't know where my implementation differs from their code samples.
Here is what I have setup at the moment (shortened to show only relevant parts):
OverviewRootComponent.js
class OverviewRootComponent extends Component {
componentDidMount() {
const { dispatch } = this.props
dispatch( fetchOrganizations() )
}
}
Actions.js
export const fetchOrganizations = () => {
console.log('Actions - fetchOrganizations');
return (dispatch) => {
console.log('Actions - return promise');
return
fetch('https://api.parse.com/1/classes/Organization', {
method: 'GET',
headers: {
'X-Parse-Application-Id': 'xxx',
'X-Parse-REST-API-Key': 'xxx',
}
})
.then( (response) => {
console.log('fetchOrganizations - did receive response: ', response)
response.text()
})
.then( (responseText) => {
console.log('fetchOrganizations - received response, now dispatch: ', responseText);
dispatch( receiveOrganizations(responseText) )
})
.catch( (error) => {
console.warn(error)
})
}
}
When I am calling dispatch( fetchOrganizations() ) like this, I do see the logs until Actions - return promise, but it doesn't seem to actually to fire off the request. I'm not really sure how how I can further debug this or what resources to consult that help me solve this issue.

I'm assuming that Redux is expecting a Promise rather than a function.. Is that true?
If so, I think your return function may not be working.
You have a new line after your return, and it's possible JavaScript is (helpfully) inserting a semicolon there.
See here: Why doesn't a Javascript return statement work when the return value is on a new line?

Related

React-native U.I Freezes and does not respond until you get response from axios

I have one problem and have searched a lot and none of the solution is working for me on the forum.
I call 6 API's on the screen (there is no loader) and there is one button at bottom which navigates user to another screen.
i have added coded in componentDidMount() ,
componentDidMount() {
this.props.API1();
this.props.API2();
this.props.API3();
}
However until i All three API's response comes in getderivedatatefromprops , the button at bottom used to navigate to another screen does not work.
this.props.API1() are redux actions defined as below
export const API1 = () => {
return (dispatch) => {
new Promise(async () => {
const body = {
method: 'GET',
url: "get url of API",
};
axios(body)
.then((response) => {
dispatch({
type: 'GETRESPONSE',
payload: { response: response.data, error: false },
});
})
.catch((error) => {
dispatch({
type: 'GETRESPONSE',
payload: { response: error?.response, error: true },
});
});
});
};
};
The problem is that you are calling async calls directly inside redux actions, which is not what redux was made to do.
You should use either redux-sage or redux-thunk if that is what you want to do.
Also, I don't see in your design why you would want to make API1, 2, and 3 redux actions. Actions are meant to store date, not for triggering events that will store data. So you could just call the API calls, and then once they return send the data to redux.
I think it is too late to answer but if somebody have this kind of problems just be sure if you do not have to many re-renders. Even if you read the documentation React Native Performance Tips. There was written that too many re-renders will cause frames to be dropped.
Just make sure that you do not have to many re-renders, use Purecomponents and don`t change state on complex components many times. I hope it would be help full.

How to handle the case where the axios response comes after the component is rendered?

I am creating a blog application in rest framework and reactjs. On the home page, under componentDidMount, I send an API call using axios to get all the articles and setState of articles to the return. As I have studied, axios works on the idea of promise such that the code doesnt proceed, if the API is not fetched for a particular component. Please tell me, if I am wrong.
Then, I send a GET call to get the writer's name, who wrote the article by the id. Though, I assumed that the axios works as a promise. But, it doesnt work that way. Now, I am not sure how to move ahead.
Here is a snippet. So, in mainBody.js, I make the api call as:
class MainBody extends Component {
state = {};
componentDidMount () {
this.get_all_articles();
};
get_writer_name (id) {
let authstr = 'Bearer ' + window.localStorage.token;
let writer_url = "http://localhost:8000/api/writer/" + id.toString() + "/";
axios.get(writer_url, { headers: { Authorization: authstr }})
.then(response => {
console.log(response.data['name'])
return response.data['name'];
})
.catch(err => {
console.log("Got error")
})
};
get_all_articles () {
let authstr = 'Bearer ' + window.localStorage.token;
axios.get("http://localhost:8000/api/articles/", { headers: { Authorization: authstr }})
.then(response => {
this.setState({articles: response.data});
})
.catch(err => {
console.log("Got error")
})
}
render () {
return (
{this.state.articles.map((article, key) =>
<ArticleView key={article.id} article={article} writer_name={this.get_writer_name(article.created_by)} />
)}
)
}
}
In articleview2, I print all the data that is present in each of the articles along with the writer's name.
My articleview class is:
class ArticleView extends Component {
state = {article: this.props.article};
componentDidMount() {
console.log(this.props.writer_name;
}
render () {
return (
<React.Fragment>
<h2>{article.title}</h2>
<p>{article.body}</p>
<span>{this.props.writer_name}</span>
</React.Fragment>
)
}
}
If you see closely, I wrote two console.log statements to get the writer names. Based on the order, first the console log present in articleview class runs, which is undefined, and thenafter the data is fetched from the API call and the console log runs which returns the correct writer name.
I wanted to know, where is the error? Also, as I noticed, there are too many API calls being made to get the writer's name multiple time for all the listed articles. What are the industry best practices for these cases?
I want to know where is the error.
When you are writing this.state.articles.map(), means you're using property map of the Array articles which may be undefined before the data is fetched that will cause you the error Cannot read property map of undefined.
Solution
Now, as the API request is asynchronous, means render method will not wait for the data to come. So what you can do is use a loader variable in the state, and set it to true as long as the request is being made, and when the response has come, make it false, and show the loader in render when this.state.loader is true, and show articles when it is false.
Or you can initialize this.state.articles with an empty array that won't cause you the error.
Also, as I noticed, there are too many API calls being made to get the writer's name multiple time for all the listed articles. What are the industry best practices for these cases?
It is extremely bad practice to make an API request in the loop. Even myself has been scolded on it once I did it in my company.
Solution
You have tell your backend engineer to provide you filter for including the writer's name in each object of the article. We use Loopback on our backend, which provides a filter for including the related model in each object internally.
Since your API calls have a lot of things in common, you should first set up an axios instance that re-uses those common features:
const api = axios.create({
baseURL: 'http://localhost:8000/api/',
headers: { Authorization: `Bearer ${localStorage.token}` }
});
Now since your MainBody needs to fetch the resources from the API asynchronously, there will be a short period where the data is not yet available. There are two ways you can handle this. Either the MainBody can be responsible for making all the calls, or it can be responsible for just making the call to get all the articles, then each of the ArticleView components can be responsible for getting the writer's name. I'll demonstrate the first approach below:
class MainBody extends Component {
state = { articles: null, error: null, isLoading: true };
async componentDidMount () {
try {
const response = await api.get('articles/');
const articles = await Promise.all(
response.data.map(async article => {
const response = await api.get(`writer/${article.created_by}/`);
return { ...article, writer_name: response.data.name };
})
);
this.setState({ articles, isLoading: false });
} catch (error) {
this.setState({ error, isLoading: false });
}
}
render () {
const { articles, error, isLoading } = this.state;
return isLoading ? 'Loading...' : error
? `Error ${error.message}`
: articles.map(article => (
<ArticleView
key={article.id}
article={article}
writer_name={article.writer_name}
/>
)
);
}
}

Any way at all to specify a callback to wrap the response with?

So I am trying to develop a search bar component in a React application where you can type in a users last name and that request will go to the Behance API and pull up that users data.
I am stuck on this:
axios
.get(API_URL + 'users?q=' + 'matias' + '&client_id=' + API_KEY + '&callback=')
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
alert(error.message);
});
I have tried wrapping the above in a const userSearch = () => {}, but that takes me a step farther from my goal. With the above I actually do get 200 statuses, but there is the CORS issue. I just can't seem to put together a callback that is not undefined in there, nevermind that this is a search bar implementation so I am going to have to refactor the above. I was just wanting to see some data returned.
One of the nicest things in Axios, is the seperation between request argument.
For example, the url should be only URL: API_URL + '/users'.
The parameters you want to pass, should be sent as an object.
The promise of the axios get, is the callback you are looking for.
Therefore, your request should look like this:
axios.get(API_URL + 'users', {
params: {
q: 'matias',
client_id: API_KEY,
}
})
.then(response => {
- success callback actions -
})
.catch(error => {
- error callback actions -
});
So I had refactored my axios code and I was still getting CORS errors, but after reading a couple of blogs online saying that with fetch() and jQuery you could get around that, in particular this SO article: Loading Data from Behance API in React Component
I actually duplicated Yasir's implementation like so:
import $ from 'jquery';
window.$ = $;
const API_KEY = '<api-key>';
const ROOT_URL = `https://api.behance.net/v2/users?client_id=${API_KEY}`;
export const FETCH_USER = 'FETCH_USER';
export function fetchUser(users) {
$.ajax({
url: `${ROOT_URL}&q=${users}`,
type: 'get',
data: { users: {} },
dataType: 'jsonp'
})
.done(response => {})
.fail(error => {
console.log('Ajax request fails');
console.log(error);
});
return {
type: FETCH_USER
};
}
And sure enough, no more CORS error and I get back the users data in my Network > Preview tabs. Not very elegant, but sometimes you are just trying to solve a problem and at wits end.

How do I make an HTTP request in react-redux?

I am just getting started with react and I'm a bit lost. I'm trying to make a login page and make a http post request. Right now I'm just trying to get any type of HTTP request working, so I'm using request bin and I found this basic action in the docs for an npm package (https://www.npmjs.com/package/redux-react-fetch):
export function updateTicket(ticketId, type, value){
return {
type: 'updateArticle',
url: `http://requestb.in/1l9aqbo1`,
body: {
article_id: ticketId,
title: 'New Title'
},
then: 'updateTicketFinished'
}
}
So, after writing an action, what do I do? How do I actually get my app to call on and use that action? The docs for the npm package mention something about setting a state in my store, is that something I need to set up first?
You can try any of the following. I have used both fetch and axios they work amazingly well. Yet to try superagent.
For making requests you can either use fetch with
fetch-polyfill for compatibility across all browsers (link)
Axios library. (link)
Superagent with promises.(link)
If you use fetch you would need to use polyfill since it is not supported in IE and safari yet. But with polyfill it works pretty well. You can check out the links for how you can use them.
So what you would doing is in your action creator you can call an API using any of the above.
FETCH
function fetchData(){
const url = '//you url'
fetch(url)
.then((response) => {//next actions})
.catch((error) => {//throw error})
}
AXIOS
axios.get('//url')
.then(function (response) {
//dispatch action
})
.catch(function (error) {
// throw error
});
So that was for the API call, now coming to the state. In redux there is one state which handles your app. I would suggest you should go through redux basics which you can find here . So once your api call succeeds you need to update your state with the data.
Action to fetch data
function fetchData(){
return(dispatch,getState) =>{ //using redux-thunk here... do check it out
const url = '//you url'
fetch(url)
.then (response ) => {dispatch(receiveData(response.data)} //data being your api response object/array
.catch( error) => {//throw error}
}
}
Action to update state
function receiveData(data) {
return{
type: 'RECEIVE_DATA',
data
}
}
Reducer
function app(state = {},action) {
switch(action.types){
case 'RECEIVE_DATA':
Object.assign({},...state,{
action.data
}
})
default:
return state
}
}

Better ways to handle REST API call in Redux

In my app, I need to call several REST API endpoints:
// The UI Class
class LoginForm extends Component {
handleSubmit(){
store.dispatch(login(username, password));
}
}
// An action
function login(username, password){
return dispatch => {
fetch(LOGIN_API, {...})
.then(response => {
if (response.status >= 200 && response.status < 300){
// success
} else {
// fail
}
})
}
}
The gist is above and easy to understand. User triggers an action, an ajax call to the corresponding endpoint is made.
As I am adding more and more API endpoints, I end up with a bunch of functions similar to the skeleton of the login function above.
How should I structure my code in such a way that I don't repeat myself with duplicate ajax functions?
Thanks!
I strongly suggest you to read this popular github sample project. At first it is hard to understand but don't worry and continue to read and realize what is happening in that.
It uses very clear and simple way to handle all of your API calls. when you want to call an API, you should dispatch an action with specific structure like this:
{
types: [LOADING, SUCCESS, FAIL],
promise: (client) => client.post('/login', {
data: {
name: name
}
})
}
and it will handle these kind of actiona by a custom middleware.
The way I handle a similar situation is to have 2 wrapper for API calls:
function get(url) {
return fetch(url)
.then(response => {
if(response.status >= 200 && response.status < 300) {
return response
}
else {
let error = new Error(response.statusText)
error.response = response
throw error
}
})
.then(response=> response.json())
}
This wrapper will take a url and return the json data. Any error that happens (network, response error or parsing error) will be caught by the .catch of get
A call basically looks like that:
get(url)
.then(data => dispatch(someAction(data)))
.catch(error => dispatch(someErrorHandler(error)))
I also have a post wrapper that in addition sets the header for CSRF and cleans the data. I do not post it here as it is quite application-related but it should be quite ovious how to do it.

Resources