Wednesday, December 20, 2017

Problems attempting to return api data being in a class

Leave a Comment

I built an api that returns me the units that I have registered in a DB but when trying to return these are not reviewing

import React, { Component } from 'react'; import axios from 'axios';  const api = {    units: () => {     axios.get('http://192.168.0.23/api/public/api/units')     .then(response => {       console.log(response);         return response.data.data     }).catch((error) => {        console.log(error.message)     });     return 'Error'   },  };  export default api; 

The response displays the data correctly, the response.data.data if used in a file with a class I can set a state units = [] and with a setState return the units

However I would like to create this file for the api returns, however as I do not use a class so I understand I can not use the state.

Is there any way of without the class returning this data, or keeping in a variable or something of the sort?

Or use setState right here without a class?

Or in the latter case build a class to be returned in another class that will call the function? Ex:

import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native';  import api from './units';  export default class App extends Component {    render() {     return (       <View style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>         <Text>Units: {api.units()}</Text>       </View>     );   } } 

I believe that these would be the ways to solve my problem, but if someone else knows otherwise than putting the api call in the same file as the final class may be too.

I tried that way too:

async function getUnits() {      return await axios.get('http://192.168.0.23/api/public/api/units'); }  getUnits.then((response) => {   console.log(response);     return response.data.data }).catch((response) => {    console.log(response) }); 

But the error indicated that getUnits.then is not a function

Even with a normal function did not work:

function units() {   axios.get('http://192.168.0.23/api/public/api/units')   .then(response => {     console.log(response);       return response.data.data   }).catch((error) => {      console.log(error.message)   });   return 'Error' }; 

4 Answers

Answers 1

Use the following code, It will help you.

Normally we would just load the data and then render our app. But React expects some initial state for all that data and needs to update itself once that AJAX request is complete. For this reason, you need to store all that data in the app's state.

Here's how that looks:

      import React, { Component } from 'react';       import { Platform, StyleSheet, Text, View } from 'react-native';       import axios from 'axios';         export default class App extends Component {           constructor(props) {           super(props);            this.state = {units: []};         }          componentDidMount() {           this.getUnits();         }          getUnits() {           axios.get('http://192.168.0.23/api/public/api/units')           .then(response => {             this.setState({ units: response.data.data }));           }).catch((error) => {              console.log(error.message)           });         }          render() {           const unit = this.state.units.map((item, i) => (             <div>               <h1>{ item.example }</h1>             </div>           ));            return (             <View style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>               <Text>Units: {unit}</Text>             </View>           );         }       } 

And If you want subcomponents to pass that data down to. Here's what that looks like:

          const unit = this.state.units.map((item, i) => (             <div>               <h1>{ item.example }</h1>              <Test unitData={item} />             </div>           )); 

In different Test component

        class Test extends Component { //use the data in a class component            constructor(props) {             super(props);           }            render() {             const unit = this.props.unitData              return (               <div className="test">                <h1>{unit.example}</h1>               </div>             );           }         } 

Please check example to here, which will help you to resolve your issue.

Examples:

  1. react-native-redux-example
  2. basic-react-native-redux-example

Hope this will help you!!

Answers 2

First off in this snippet

async function getUnits() {      return await axios.get('http://192.168.0.23/api/public/api/units'); }  getUnits.then((response) => {   console.log(response);     return response.data.data }).catch((response) => {    console.log(response) }); 

You have made a mistake, you should be calling the getUnits function here eg

getUnits().then(.....) 

Regarding whether or not to use a stateful component or a functional component, have a look at the documentation here for some explanation about the differences.

The first page discusses functional components which do not manage their own state. The second page discusses state.

With your situation one of the components will have to make the AJAX call, so one component must be responsible for the state. If you are using Redux the conversation becomes much different. However if you aren't using Redux, you generally will have the state in a high level stateful component with stateless components beneath it. The base component will pass the data (from your AJAX request) to its stateless children by way of props (which are read-only).

So finally we can get to some code! In your api object, let's support a way to pass data back to the caller. Here we are adding onLoad and onError callback parameters which accept functions as arguments. When a successful response arrives, the onLoad callback is called with the data from the API. When an error occurs, the onError callback is called with the error message.

import axios from 'axios';  const api = {   units: (onLoad, onError) => {     axios.get('http://192.168.0.23/api/public/api/units')     .then(response => {         onLoad(response.data.data);     }).catch((error) => {          onError(error.message);     });   }, };  export default api; 

Now we'll make your App component stateful (remember, it can have stateless children that it passes data via props). Note we are invoking the API call right before the component will mount, because we call it in the componentWillMount lifecycle method of React. This will not prevent the component from rendering though since your api object is making an asynchronous call, this merely invokes the API request and let's your component render immediately. Once the data arrives, your api object will call the onLoad callback, which is the onApiLoad method of your App component. The App component will then update its state and re-render!

import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native'; import api from './units';  export default class App extends Component {    constructor(props) {     super(props);      this.state = {units: []};   }    componentWillMount() {     api.units(this.onApiLoad, this.onApiError);   }    onApiLoad(units) {     this.setState({       units: units     });   }    onApiError(errorMessage) {     // TODO Something meaningful with error message   }    render() {     const unit = this.state.units.map((item, i) => (       <div>         <h1>{ item.example }</h1>       </div>     ));      return (       <View style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>         <Text>Units: {unit}</Text>       </View>     );   } } 

Answers 3

If I got your problem then this is what you need to do:

change this:

import React, { Component } from 'react'; import axios from 'axios';  const api = {  units: () => {   axios.get('http://192.168.0.23/api/public/api/units')     .then(response => {       console.log(response);       return response.data.data     }).catch((error) => {     console.log(error.message)   });   return 'Error' },  };  export default api; 

to:

import axios from 'axios';  const api = {  units: () => {   return axios.get('http://192.168.0.23/api/public/api/units')     .then(response => {       console.log(response);       return response.data.data     }).catch((error) => {     console.log(error.message)   });   return 'Error' },  };  export default api; 

just added a return statement there before axios

then use it in your component like

import api from './api'; .... .... .... componentDidMount() {   api.units().then(units => this.setState({ units })) } .... .... .... 

Answers 4

I encountered this issue of async callback in my initial days of programming with javascript, where I found it very difficult to pass results between the files using pure functions. There is something I can suggest you, that you can use anywhere with async programming in javascript and has nothing to do with react.js

import React, { Component } from 'react'; import axios from 'axios';  const api = {    units: (callback) => {     axios.get('http://192.168.0.23/api/public/api/units')     .then(response => {       console.log(response);         callback(response.data.data);     }).catch((error) => {        console.log(error.message)     });     return 'Error'   },  };  export default api; 

And inside your App component:

import React, { Component } from 'react'; import { Platform, StyleSheet, Text, View } from 'react-native';  import api from './units';  export default class App extends Component {    constructor(props) {     super(props);      this.state = {       units: [],     };   }   //Always do async tasks(side effects) in componentDidMount, I will provide you a link to know Do's and Dont's with lifecycle hooks.    componentDidMount() {     api.units((data) => this.setState({units: data}));   }    render() {     return (       <View style={{ flexGrow: 1, alignItems: 'center', justifyContent: 'center' }}>         <Text>Units: {this.state.units}</Text>       </View>     );   } } 

This is the way of passing a callback functions around when you deal with async operations in Javascript, as you can not return a evaluated result from an async function, because as soon as the result is still being evaluated your function will return something and get out of the scope, but you can use the callback handler of the async operation to finally update your view according to the evaluated result. I am only handling the success callback, you can pass error callback as well to handle the error case.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment