I need to export result of axios response on module.exports.
This is my codes:
brand.js
var axios = require('axios');
module.exports = (async function() {
try {
const { data } = axios.get('http://localhost:8000/api/v1/setting/index');
console.log(data.data.initial);
return {
name: data.data.name,
desc: data.data.description,
};
} catch (err) {
console.log(err);
}
})();
I try to import the result to be used on another file.
This is my code.
import React from 'react';
const {brand} = await require("brand.js");
class Dashboard extends Component {
render(){
const name = brand.name
const desc = brand.description;
return (
<h1>{title} | {description}</h1>
);
}
}
The result of my code is:
Can not use keyword 'await' outside an async function
This is error shown on browser:
How to solved this?
you can do like this.
// brand.js
import axios from 'axios';
export const fetchData = async () => {
let response;
try {
response = await axios.get(url, config);
} catch (e) {
// catch error
throw new Error(e.message)
}
// if success return value
return response?.data ? response?.data : null // or set initial value
}
then in your React
import { fetchData } from './path/to/fetchData';
const response = fetchData();
const MyComponent = (props) => {
return (
<div>name: {response.data.name} | desc: {response.data.description}</div>
)
}
Related
I am using Firebase and React and learning how to use React Contexts. I created a context that queries Firebase to get data from there (user's files URL's for example). The context works fine, however, I can't get the context to become asynchronous. I haven't seen any examples of this and am not sure if it's possible. My code is below.
StorageContext.js:
import React, { useContext, useEffect, useState } from 'react';
import { auth } from './FirebaseConfiguration';
import fire from './FirebaseConfig';
import { useAuth } from './AuthContext';
const StorageContext = React.createContext();
export function useStorage() {
return useContext(StorageContext);
}
export function StorageProvider({ children }) {
const { currentUser } = useAuth();
const [fileURLs, setFilesURL] = useState([]);
async function getUserData() {
var storage = fire.storage();
var storageRef = storage.ref(currentUser.uid);
storageRef
.listAll()
.then(function (result) {
result.items.forEach(function (imageRef, i) {
let temp = filesUploaded;
temp.push(imageRef.name);
setFilesUploaded(temp);
});
console.log(filesUploaded);
// console.log(getData(filesUploaded));
getData(filesUploaded);
})
.catch(function (error) {
console.log(error);
});
}
const value = { getUserData };
return (
<StorageContext.Provider value={value}>{children}</StorageContext.Provider>
);
}
Dashboard.js:
import React, { useState, useEffect } from 'react';
import { useStorage } from './Contexts/StorageContext';
export default function Dashboard() {
const { getUserData } = useStorage();
async function getData() {
await getUserData().then((data) => {
console.log(data);
});
}
useEffect(() => {
getData();
}, []);
return (
<div>
console.log('data');
</div>
The useEffect in Dashbaord.js runs fine, the problem is that getUserData() returns immediately even though it should be waiting until (and thus the .then((data) => { console.log(data) } is empty.
Is it possible to run a Context Asynchronously? Or is there another problem that I am missing?
Thanks
The reason it returns immediately is that you use then and not await. Rewrite your function to this and it should work:
async function getUserData() {
var storage = fire.storage();
var storageRef = storage.ref(currentUser.uid);
const result = await storageRef.listAll();
result.items.forEach(function (imageRef, i) {
let temp = filesUploaded;
temp.push(imageRef.name);
setFilesUploaded(temp);
});
console.log(filesUploaded);
// console.log(getData(filesUploaded));
getData(filesUploaded);
}
There is a react-component:
import React, { useState, useCallback } from 'react';
import { useHttp } from '../../hooks/http.hooks';
function Main() {
const {loading, error, request} = useHttp();
const [news, setNews] = useState(0);
const topNews = useCallback(async function() {
const data = await request('http://localhost:5500/api/news/top/2');
return data;
}, []);
console.log(topNews());
return (
<div>Hello world</div>
);
}
export default Main;
And a custom hook:
import { useState } from 'react';
export const useHttp = () => {
const [loading, setLoading] = useState();
const [error, setError] = useState();
async function request(url, { method = 'GET', body = null, headers = {} } = {}) {
setLoading(true);
try {
const response = await fetch(url, { method, body, headers });
const data = await response.json();
if (!response.ok) {
throw new Error(data.msg || 'unhandled error');
}
return data;
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
return { loading, request, error }
}
Starting it throw error:
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
And so many Promises in console:
As I understood, when loading is changing Main is rendering, because I added a useCallback() but it's not working. How to get rid of looping right?
Inside your React Component, request should be called inside useEffect instead of useCallback. If you keep it that way, the loop looks like this :
Component render
request is called
request update component states
states change force render, so go back to bullet #2
You should change your code to something like this :
import React, { useState, useEffect } from 'react';
import { useHttp } from '../../hooks/http.hooks';
function Main() {
const {loading, error, request} = useHttp();
const [news, setNews] = useState([]);
useEffect(() => {
const data = await request('http://localhost:5500/api/news/top/2');
setNews(data);
}, [])
console.log(news);
return (
<div>Hello world</div>
);
}
export default Main;
See useEffect for more details and advanced usage.
Move the async function call to useEffect, you currently call it on every render:
function Main() {
const {loading, error, request} = useHttp();
const [news, setNews] = useState(0);
useEffect(() => {
async function topNews() {
const data = await request('http://localhost:5500/api/news/top/2');
return data;
}
setNews(topNews());
}, [])
return (
<div>{JSON.stringify(news,null,2)}</div>
);
}
I'm trying to mock an async function that is exported as a default export but all I get is TypeError: Cannot read property 'then' of undefined
What I'm trying to mock is config.js:
const configureEnvironment = async (nativeConfig) => {
return { await whatever() }
}
The file I'm testing is Scene.js:
import configureEnvironment from './config';
class Scene extends React.Component {
constructor(props) {
nativeConfig = {};
configureEnfironment(nativeConfig).then((config) => {
// Do stuff
}
}
}
And my test file is Scene.test.js:
let getScene = null;
const configureEnvironmentMock = jest.fn();
describe('Scene', () => {
jest.mock('./config', () => configureEnvironmentMock);
const Scene = require('./Scene').default;
getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(configureEnvironmentMock).toHaveBeenCalledWith(nativeConfig);
});
});
However, the result of running the test is:
TypeError: Cannot read property 'then' of undefined
I understand the issue is on the way I mock configureEnvironment but I cannot get it working.
I also tried to mock the function like:
jest.mock('./config', () => {
return {
default: configureEnvironmentMock,
};
});
But it results on:
TypeError: (0 , _config2.default) is not a function
A clean and simple way to mock the default export of a module is to use jest.spyOn in combination with functions like mockImplementation.
Here is a working example based on the code snippets above:
config.js
const whatever = async () => 'result';
const configureEnvironment = async (nativeConfig) => await whatever();
export default configureEnvironment;
Scene.js
import * as React from 'react';
import configureEnvironment from './config';
export class Scene extends React.Component {
constructor(props) {
super(props);
configureEnvironment(props.prevState).then((config) => {
// Do stuff
});
}
render() {
return null;
}
}
Scene.test.js
import React, { Fragment } from 'react';
import { shallow } from 'enzyme';
import { Scene } from './Scene';
import * as config from './config';
describe('Scene', () => {
const mock = jest.spyOn(config, 'default'); // spy on the default export of config
mock.mockImplementation(() => Promise.resolve('config')); // replace the implementation
const getScene = (previousState) => {
return shallow(
<Scene prevState={previousState}>
<Fragment />
</Scene>,
);
};
it('calls configureEnvironment with the nativeConfig', async () => {
expect.assertions(1);
const nativeConfig = {};
getScene(nativeConfig);
expect(mock).lastCalledWith(nativeConfig); // SUCCESS
});
});
You can mock anything with jest, like this
jest.mock('#material-ui/core/withWidth', () => ({
__esModule: true,
isWidthUp: jest.fn((a, b) => true),
default: jest.fn(fn => fn => fn)
}))
I'm working with Open Weather API, but when i send an request for a city data, i get network error.
Here is the action which does the job of getting the resource.
actions/index.js
import axios from "axios";
const ROOT_URL = `https://samples.openweathermap.org/data/2.5/forecast?appid=${API_KEY}`;
export const FETCH_WEATHER = "FETCH_WEATHER";
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city},us`;
const request = axios.get(url);
console.log("request:", request);
return {
type: FETCH_WEATHER,
payload: request
};
}
When i press the submit button then i can see error in the mozilla firefox console.
something like this...Error:network error
but i want the city name under the city header...
just like this...
axios.get(url) is returning promise,
conventional way is,
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city},us`;
const res = axios.get(url).then(function(res){
console.log("response:", res);
return {
type: FETCH_WEATHER,
payload: res
};
}).catch(err){
console.log(err)
}
}
OR,
Use async/await to get required result.
export async function fetchWeather(city) {
try{
const url = `${ROOT_URL}&q=${city},us`;
const res = await axios.get(url);
console.log("request:", res);
return {
type: FETCH_WEATHER,
payload: res
};
}catch(err){
console.log(err)
}
}
In your actions/index.js put this:
import axios from "axios";
const API_KEY = "YOUR API KEY GOES HERE ...";
const ROOT_URL = `https://api.openweathermap.org/data/2.5/forecast?appid=${API_KEY}&units=metric`;
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city) {
const url = `${ROOT_URL}&q=${city}`;
const request = axios.get(url);
return {
type: FETCH_WEATHER,
payload: request,
};
}
In your reducers/reducer_weather.js put this:
import { FETCH_WEATHER } from "../actions/index";
export default function (state = [], action) {
if (action.error) {
return state;
}
switch (action.type) {
case FETCH_WEATHER:
return [action.payload.data, ...state];
}
return state;
}
Additionally, make sure to include your weather reducer inside of you root reducer, for example:
reducers/index.js
import { combineReducers } from "redux";
import WeatherReducer from "./reducer_weather";
const rootReducer = combineReducers({
weather: WeatherReducer
});
export default rootReducer;
You have built your URL wrong way, and you can not query your initial URL, it is just a sample ready for download.
import axios from 'axios';
const ROOT_URL = 'http://api.openweathermap.org/data/2.5/forecast';
export const FETCH_WEATHER = 'FETCH_WEATHER';
export function fetchWeather(city) {
const url = `${ROOT_URL}?q=${city},us&APPID=${API_KEY}`;
const request = axios.get(url);
console.log('request:', request);
return {
type: FETCH_WEATHER,
payload: request
};
}
Regards.
I'm trying to understand mobx implementation in React. I used create react app and update default configuration to use decorators. Then I created a simple store like this :
EDIT : after Ben Hare (thanks to him !) reply I updated my code like this :
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import MessageStore from "./store/messages";
ReactDOM.render(<App store={new MessageStore()} />,
document.getElementById('root'));
** App.js **
import React from "react";
import { observer } from "mobx-react";
#observer
export default class App extends React.Component {
constructor(props) {
super(props);
this.store = props.store;
}
render() {
return <ul>
{ this.store.allMessages.map((msg) => {
return <li key={msg}>{msg}</li>
})}
</ul>
}
}
messages.js
import {action, observable, computed} from "../../node_modules/mobx/lib/mobx";
export default class MessageStore {
#observable messages = ["My first message"];
constructor() {
setInterval(() => {
// Add some random messages every second
this.addMessage(Math.random());
}, 1000);
}
#action addMessage(msg) {
this.messages.push(msg);
}
#computed get allMessages() {
return this.messages;
}
}
The first message is displayed, but component never update when setInterval add message into the store. Can you help me ?
Works for me:
https://codesandbox.io/s/LgQXNBnNW
Did you see any errors in the browser log or terminal?
Check my approach please. Maybe it will help:
MobX store:
import { action, observable, runInAction } from 'mobx'
class DataStore {
#observable data = null
#observable error = false
#observable fetchInterval = null
#observable loading = false
//*Make request to API
#action.bound
fetchInitData() {
const response = fetch('https://poloniex.com/public?command=returnTicker')
return response
}
//*Parse data from API
#action.bound
jsonData(data) {
const res = data.json()
return res
}
//*Get objects key and push it to every object
#action.bound
mapObjects(obj) {
const res = Object.keys(obj).map(key => {
let newData = obj[key]
newData.key = key
return newData
})
return res
}
//*Main bound function that wrap all fetch flow function
#action.bound
async fetchData() {
try {
runInAction(() => {
this.error = false
this.loading = true
})
const response = await this.fetchInitData()
const json = await this.jsonData(response)
const map = await this.mapObjects(json)
const run = await runInAction(() => {
this.loading = false
this.data = map
})
} catch (err) {
console.log(err)
runInAction(() => {
this.loading = false
this.error = err
})
}
}
//*Call reset of MobX state
#action.bound
resetState() {
runInAction(() => {
this.data = null
this.fetchInterval = null
this.error = false
this.loading = true
})
}
//*Call main fetch function with repeat every 5 seconds
//*when the component is mounting
#action.bound
initInterval() {
if (!this.fetchInterval) {
this.fetchData()
this.fetchInterval = setInterval(() => this.fetchData(), 5000)
}
}
//*Call reset time interval & state
//*when the component is unmounting
#action.bound
resetInterval() {
if (this.fetchInterval) {
clearTimeout(this.fetchInterval)
this.resetState()
}
}
}
const store = new DataStore()
export default store