I am beginner in JS and React.
I have a problem:
import React from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const jsonApi = new JsonApi(); //it is my class which has methods
//to manage with data(get,post,etc);
const user = jsonApi.getUser(); //returns promise,but i need an object with data!
//promise has such view:
//[[Prototype]]: Promise
//[[PromiseState]]: "fulfilled"
//[[PromiseResult]]: Object !!!!i need this data!!!!
console.log(user); //Promise.
/* i know that a i can do so:
user.then((data) => console.log(data));
but,using this way,i can only log!But i need an object with data!
*/
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
I understand that i need to use await before const user = jsonApi.getUser();
but we can do that only inside async functions.
So,i tried to do that: const UserPage = async () => { }
but i had a mistake:
In order to perform side effects in react you should consider using useEffect hook. After the effect you need to store the data retrieved in react state by using the useState hook. In the end your code would look like below:
import React, { useState, useEffect } from "react";
import JsonApi from "../../services/jsonApi";
const UserPage = () => {
const [user, setUser] = useState(null);
useEffect(() => {
const jsonApi = new JsonApi();
jsonApi.getUser().then((user) => {
setUser(user);
});
}, []);
if (!user) return null;
return (
<div className="app">
<h1>{user.name}</h1>
<p>Here are info about users!</p>
</div>
);
};
export default UserPage;
Keep in mind that the user is not populated until you async getUser resolves, so you have to handle the case where user data are not yet present, either by rendering nothing (null) or by showing some loading state in between.
Related
How can I create a component for Axios that I can use in different places with different values ??
I got stuck, what should I do?
This is what I have achieved so far
thank you for Helping
import axios from "axios";
const Axios = (props) => {
const [posttitle, postbody] = useState([]);
const [postuserid, postid] = useState([]);
const fetchData = () => {
const { postbodyapi } = props.postbodyapi;
const postuseridapi = "https://nba-players.herokuapp.com/players/james/lebron";
const getbody = axios.get(postbodyapi);
const getuseid = axios.get(postuseridapi);
axios.all([getbody, getuseid]).then(axios.spread((...allData) => {
const databody = allData[0].data.first_name;
const datauseid = allData[1].config.url;
postbody(databody);
postid(datauseid);
}))
}
useEffect(() => {
fetchData()
}, [])
return (
<div className="App">
{posttitle}
<img src={postuserid} alt="asd"/>
</div>
);
}
export default Axios;
You should create a custom hook.
Create a hook called for example useAxios and hold only the fetching method inside of it, and the return state from that hook should be just data.
you can make it so it takes params like "URL, data, method", or make a few smaller hooks like useAxiosGet, useAxiosPost.
If you make a few smaller it will be easier to read and change something if needed.
Here is how I did it, an example of one specific Axios custom hook, use this for example to see how to build it.
useGetCar.js // custom axsios hook
import axios from 'axios';
const useGetCar = async (url, id) => {
const result = await axios.post(url, {id: id});
return result.data[0];
}
export default useGetCar
car.js // page component that displays data
import useGetCar from "#hooks/useGetCar";
let car_id = 1; // some that i send to api
// this function here is not exact from my code,
//but I just wanted to provide you an example.
// I didn't include my original code because it is
//from next.js app and I don't want to confuse u with that
async function getData() {
let car = await useGetCar(`http://localhost/get_car.php`, car_id);
return car;
}
Hope you understood what I'm saying, and I did not confuse you.
Feel free to ask anything if you don't understand something clearly.
Happy coding.
I am making an weather app with API, I am successfully receiving the data with API in the function but I am not able to take it out of the function
here is the code
import React, {useState} from 'react'
import {Text, View} from 'react-native'
const axios = require('axios');
let HomeScreen =() => {
let key = "XXXX"
axios.get(`https://api.weatherapi.com/v1/current.json?key=${key}&q=London&aqi=no`)
.then(function (response) {
// handle success
console.log(response)
})
return(
<Text>This is {response}</Text>
)
}
export default HomeScreen
If you want to simply return data from the API use a normal JS function, not a React component.
function getData() {
return axios(`https://api.weatherapi.com/v1/current.json?key=${key}&q=London&aqi=no`)
}
It will return a promise which you can then use somewhere else.
async main() {
const data = await getData();
}
If you want your component to render data retrieved from the API you need to do things differently.
Use useEffect to run once when the component is mounted, and use useState to store the data once it's been retrieved. The state will then inform the JSX how to render.
Note that the response from the API is an object with location and current properties. You can't just add that to the JSX because React won't accept it. So, depending on what value you need from the data, you need to target it specifically.
Here's an example that returns the text value from the condition object of the current object: "It is Sunny".
const { useEffect, useState } = React;
function Example() {
// Initialise your state with an empty object
const [data, setData] = useState({});
// Call useEffect with an empty dependency array
// so that only runs once when the component is mounted
useEffect(() => {
// Retrieve the data and set the state with it
async function getData() {
const key = 'XXX';
const data = await axios(`https://api.weatherapi.com/v1/current.json?key=${key}&q=London&aqi=no`)
setData(data.data);
}
getData();
}, []);
// If there is no current property provide a message
if (!data.current) return <div>No data</div>;
// Otherwise return the current condition text
return (
<p>It is {data.current.condition.text}</p>
);
}
I have a straightforward react component that looks so in AllWords.js :
import React, { useEffect, useState } from 'react';
import consts from '../../constants/Constants.js';
function AllWords() {
const [words, setWords] = useState([]);
async function fetchData(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
I would like to refactor the fetchData() method out of the component into another file (basically a separate .js file that holds the fetch call).
What I would like is to have created a file titled FetchAllWords.js under src/actions/ & then import it. & use that.
I have several questions :
do I need to set the state in the FetchAllWords.js and then useSelector to extract the state in AllWords.js?
in FetchAllWords.js do I need to usedispatch to dispatch a method call setting the state? I would like to just setState in FetchAllWords.js and then extract it in AllWords.js. This is what I have so far:
import consts from '../constants/Constants.js';
import { useState } from 'react';
async function FetchAllWords(){
const [words, setWords] = useState([]);
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
}
export default FetchAllWords;
I am unsure how to import this and use it in AllWords.js. I am using the following statement :
import wordList from '../../actions/FetchAllWords';
Then I am trying to use wordList as a handle to the file '../../actions/FetchAllWords.js' & attempting to access the async function FetchAllWords so wordList.FetchAllWords();
Firstly , the editor (VSCode) won't let me see the function despite the import call.
Secondly I am getting an error (something like) :
TypeError: _actions_FetchAllWords_js__WEBPACK_IMPORTED_MODULE_3__.default.FetchAllWords is not a function
Any insight or help would be appreciated since rather uneasy with JS & React.
The github repo is : https://github.com/mrarthurwhite/hooks-p5-react-redux
EDIT: As per David's suggestions :
So AllWords.js React component is :
import React, { useEffect, useState } from 'react';
import wordList from '../../services/Fetch.js';
function AllWords() {
const [words, setWords] = useState([]);
function fetchData(){
wordList.fetchAllWords().then(
data => setWords(data)
);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
And Fetch.js is :
import consts from '../constants/Constants.js';
class Fetch {
async fetchAllWords(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
}
export default Fetch;
No, don't worry about state in the external file. Just focus on the one thing it should do, perform the AJAX operation. At its simplest it's just a function, something like:
import consts from '../../constants/Constants.js';
const fetchAllWords = async () => {
const response = await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
export default fetchAllWords;
You can even make it a class which contains this function, if you plan on adding other service operations as well. (Fetch specific word? Find word? etc.) The point is that this does just one thing, provide data. Let the React components handle React state.
Within the component you'd just use that to get your data. Something like:
import React, { useEffect, useState } from 'react';
import fetchAllWords from '../../services/FetchAllWords.js';
function AllWords() {
const [words, setWords] = useState([]);
useEffect(() => {
fetchAllWords().then(w => setWords(w));
}, []);
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
Overall it's a matter of separating concerns. The service performs the AJAX operation and returns the meaningful data, internally concerned with things like JSON deserialization and whatnot. The React component maintains the state and renders the output, internally concerned with updating state after useEffect runs and whatnot.
I'm creating my first MERN stack application, and trying to implement a simple API that calls my express server from my React front-end components. I have the API working on the back end, and it is sending the data correctly through fetch(), but I'm having trouble resolving the promise from fetch() in my React component, with the call not stopping firing. My code looks as follows (assuming as of right now all API calls return a dummy format like { title: 'foo', ... }:
import React, { useState } from 'react';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
const getApiData = async (route) => {
try {
let apiData = await fetch(route);
let apiDataJson = await apiData.json();
return apiDataJson;
} catch (err) {
throw new Error('Error on fetch', {
error: err
})
}
}
var retrieve_data = async (route, setterCallback) => {
await getApiData(`/api/${route}`).then((data) => {
console.log('Data retrieved from API')
setterCallback(<div>{data.title}</div>)
}).catch(() => {
setterCallback(<div>ERROR</div>)
})
}
const MyComponent = () => {
const [innerDiv, setinnerDiv] = useState(0);
let data = retrieve_data('myEndpoint', setinnerDiv);
return(
<div>
<h1>Data Retrieved in MyComponent:</h1>
{innerDiv}
</div>
);
}
When I compile the above the component successfully renders (i.e. <MyComponent /> looks like:
<div>
<h1>Data Retrieved in MyComponent:</h1>
<div>foo</div>
</div>
However, then then block keeps executing (i.e. the 'Data retrieved from API' logs to the console hundreds of times/second until I close the application. How can I stop this from executing once it has set the component? Thanks!
You need to useEffect to stop the component from re-rendering. Try something like this.
const MyComponent = () => {
const [innerDiv, setinnerDiv] = useState(0);
useEffect(() => {
retrieve_data('myEndpoint', setinnerDiv);
}, []);
return(
<div>
<h1>Data Retrieved in MyComponent:</h1>
{innerDiv}
</div>
);
}
Lets say I have a component that renders a PivotGrid component. This PivotGrid takes some data, an array of objects.
function My_component(props) {
return <PivotGrid dataSource={props.data} />
}
However, the data prop that I want to pass in is the result of an async function.
const get_data = async () => {
return await o(url, options).get('foo').query({})
}
ReactDOM.render(<My_component data={get_data()}/>, document.getElementById('root'));
What happens is that the component renders before the promise from get_data() is resolved, and the PivotGrid has no data.
What I would like to happen is for the component to re-render when the promise resolves and actually returns data. I've tried variations of React's useState() to treat props.data as a state variable, so that when the promise returns the state would change the the component would update. But this has not worked yet.
const [gridData, setGridData] = useState(props.data);
props.data.then((r) => {
setGridData(props.data)
})
Attempts like the above all fail. What is the best way to achieve this functionality, where the component re-renders when prop.data resolves and actually holds the data I want?
Using hooks and the container component for My_component should work.
my-component-container.js:
import React, {useState, useEffect} from 'react'
import My_component from './my-component'
export default () => {
const [data, setData] = useState(null)
useEffect(async () => {
const fetchData = async () => {
const result = await o(url, options).get('foo').query({})
setData(result);
};
fetchData();
}, [])
return <My_component dataSource={data} />
}
In your entry point:
import My_component_container from './my-component-container'
ReactDOM.render(<My_component_container />, document.getElementById('root'))
Why not change to a stateful component like below
class My_component extends React.Component {
state = {};
componentDidMount(){
this.get_data()
}
get_data = async () => {
const data = await o(url, options).get('foo').query({});
this.setState({ data });
}
render() {
const { data } = this.state;
return <PivotGrid dataSource={data} />
}
}