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:
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.
0 comments:
Post a Comment