im trying to get api data into a chart in react js i was using a local array to use like sample values
now im trying to pass an api data to inside the chart but i get the data.slice is not a function error and i dont know why, someone here can help me?
PS: i call the function inside an use effect hook
function
const getPeso = async () => {
const jwt = sessionStorage.getItem('Token')
const user = jwtDecode(jwt)
const token = sessionStorage.getItem('Token')
console.log(token)
const response = await fetch("http://localhost:8080/animals/animal/client?clientId=" + user.id, {
method: "get",
headers: {
"Authorization": `${token}`,
},
})
const data = await response.json();
setPeso(data)
};
Chart
<div className='pesoGraph'>
<h1>Pesagem</h1>
<a>Mês</a>
<a style={Styles}>Ano</a>
<LineChart
width={650}
height={300}
data={setPeso}
margin={{
top: 20,
right: 50,
left: 50,
bottom: 70
}}
>
<CartesianGrid vertical={false} />
<XAxis dataKey="data" axisLine={false} tickLine={false} tickMargin={10} color={'#494949'} fontWeight={'500'} />
<YAxis dataKey='peso' domain={['dataMin', 'dataMax']} tickCount={4} axisLine={false} tickLine={false} tickMargin={10} color={'#494949'} fontWeight={'500'} />
<Tooltip />
<Line
type="monotone"
dataKey="peso"
stroke="#000000"
activeDot={{ r: 8 }}
/>
</LineChart>
</div>
From this line setPeso(data) it is clear that setPeso(data) is a function.
You are then passing it to the chart component:
<LineChart
width={650}
height={300}
data={setPeso} // <-- here
Based on the error message the chart component is expecting an array as data.
Your code should be something like
const [peso, setPeso] = useState([]);
// ... other code
<LineChart
width={650}
height={300}
data={peso}
where peso is an array in state.
Related
Hi so my main question is when making a api call to https://newsdata.io, i want to access the results method on the object the api returns. However react native is saying results is undefined. Why cant i see the data object but not the methods attached to the data variable.
import React from 'react'
import { Text, View, Image } from 'react-native'
export const Feed = () => {
async function data() {
const response = await fetch('https://newsdata.io/api/1/news?apikey=pub_11306c8c5e2932eab7155edacbc6339247174&q=web%203')
const data = await response.json()
const results = data.results;
const imageURLDATA = results.forEach(element => {
console.log(element.image_url)
})
}
data()
return (
<View>
<Text style={{fontSize: 40, fontWeight: "700", marginTop: 20}}>Feed</Text>
{results.forEach(element => {
<View>
< Image source={{
uri: `${element.image_url}`
}}/>
</View>
})}
</View>
)
}
You need to take the component lifecycle into consideration. Use useState to create a variable that trigger component rerenders and useEffect to call functions at certain events:
import React, { useState, useEffect } from 'react';
import { View, Image, Text } from 'react-native';
export const Feed = () => {
const [feedData, setFeedData] = useState([]);
async function getData() {
// you may want to change your api key
const response = await fetch(
'https://newsdata.io/api/1/news?apikey=pub_11306c8c5e2932eab7155edacbc6339247174&q=web%203'
);
const data = await response.json();
const results = data.results;
setFeedData(results);
}
//will call provided function when items in array is updated
useEffect(() => {
console.log('Feed data updated')
feedData.forEach((element) => {
console.log(element.image_url);
});
}, [feedData]);
// will call provided function once after first render
useEffect(() => {
getData();
}, []);
return (
<View>
<Text style={{ fontSize: 40, fontWeight: '700', marginTop: 20 }}>
Feed
</Text>
{/*forEach returns null, map returns value*/}
{feedData.map((element) => (
<View>
<Image
source={{
uri: `${element.image_url}`,
}}
// provide width to element or it wont render
style={{width:100,height:100}}
/>
</View>
)
)}
</View>
);
};
export default Feed;
Here's a demo
I have a problem and I can't see the error.
My app made with Next.js, uses an API to fetch values of a currency.
Locally, the date and prices are updated when they are modified. But in production it is not working, it only shows the last data that was loaded when the deployment was done.
I use getStaticProps in the index to fetch the data for each currency category.
export default function Home({ oficial, blue, bolsa, turista, contadoliqui }) {
return (
<Layout>
<Box p={10}>
<Stack
display={{ md: "flex" }}
spacing={8}
direction={["column", "column", "row"]}
>
<DolarOficialCard oficial={oficial} />
<DolarBlueCard blue={blue} />
</Stack>
<Stack
display={{ md: "flex" }}
spacing={8}
mt={8}
direction={["column", "column", "row"]}
>
<DolarBolsaCard bolsa={bolsa} />
<DolarContado contadoliqui={contadoliqui} />
</Stack>
<Box direction={["column", "column", "row"]} spacing={8} mt={8}>
<DolarTuristaCard turista={turista} />
</Box>
<Box align="center" mt={50}>
<Alert status="info" justifyContent="center">
<AlertIcon />
Última Actualización: {oficial.fecha}
</Alert>
</Box>
</Box>
</Layout>
);
}
export const getStaticProps = async () => {
const oficial = await getDolarOficial();
const blue = await getDolarBlue();
const bolsa = await getDolarBolsa();
const turista = await getDolarTurista();
const contadoliqui = await getDolarLiqui();
return {
props: {
oficial,
blue,
bolsa,
turista,
contadoliqui,
},
};
};
getStaticProps means your page is a static page, only generated once. If you want to always fetch the latest data, you need to use getServerSideProps
Otherwise you can use incremental static regeneration. You need your getStaticProps to revalidate in a predefined "seconds".
export const getStaticProps = async () => {
const oficial = await getDolarOficial();
const blue = await getDolarBlue();
const bolsa = await getDolarBolsa();
const turista = await getDolarTurista();
const contadoliqui = await getDolarLiqui();
return {
props: {
oficial,
blue,
bolsa,
turista,
contadoliqui,
},
revalidate: 10, // Revalidate every 10 seconds with new data.
};
};
In development (next dev), getStaticProps will be called on every
request.
This is the reason why it works locally but not in production.
I just implemented a global search in my website and I started having issues with React-Router. It is not updating the view if the url changes parameters.
For example, navigating from /users/454545 to /teams/555555 works as expected. However, navigating from /teams/111111 to teams/222222 changes the url but the component is still /teams/111111.
Here is my code fo the Search Input field.
const SearchResult = ({ id, url, selectResult, text, type }) => (
<Row key={id} onClick={() => selectResult(url)} width='100%' padding='5px 15px 5px 15px' style={{cursor: 'pointer'}}>
<Column alignItems='flex-start' style={{width: '100%'}}>
<Label textAlign='left' color='#ffffff'>{text}</Label>
</Column>
<Column style={{width: '100%'}}>
<Label textAlign='right' color='#ffffff'>{type}</Label>
</Column>
</Row>
)
const SearchInput = (props) => {
const { isSearching, name, onChange, onClear, results } = props;
return (
<Section width='100%' style={{display: 'flex', position: 'relative'}}>
<Wrapper height={props.height} margin={props.margin}>
<i className="fas fa-search" style={{color: 'white'}} />
<input id='search_input' placeholder={'Search for a team, circuit, or user'} name={name} onChange={onChange} style={{outline: 'none', backgroundColor: 'transparent', borderColor: 'transparent', color: '#ffffff', width: '100%'}} />
{onClear && !isSearching && <i onClick={onClear} className="fas fa-times-circle" style={{color: '#50E3C2'}} />}
{isSearching &&
<Spinner viewBox="0 0 50 50" style={{marginBottom: '0px', height: '50px', width: '50px'}}>
<circle
className="path"
cx="25"
cy="25"
r="10"
fill="none"
strokeWidth="4"
/>
</Spinner>
}
</Wrapper>
{results && <Section backgroundColor='#00121A' border='1px solid #004464' style={{maxHeight: '400px', position: 'absolute', top: '100%', left: '0px', width: '97%', overflowY: 'scroll'}}>
<Section backgroundColor='#00121A' style={{display: 'flex', flexDirection: 'column', padding: '15px 0px 0px 0px', justifyContent: 'center', alignItems: 'center', width: '100%'}}>
{results.length === 0 && <Text padding='0px 0px 15px 0px' color='#ffffff' fontSize='16px'>We didn't find anything...</Text>}
{results.length !== 0 && results.map(r => <SearchResult selectResult={props.selectResult} id={r._id} url={r.url} text={r.text} type={r.type} />)}
</Section>
</Section>}
</Section>
)
}
export default SearchInput;
The parent component is a nav bar which looks something like this. I've slimmed it down for readability.
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import SearchInput from '../shared/inputs/SearchInput';
const TopNav = (props) => {
const [search, setSearch] = useState(null);
const [searchResults, setSearchResults] = useState(null);
const debouncedSearchTerm = useDebounce(search, 300);
const [isSearching, setIsSearching] = useState(false);
function clearSearch() {
document.getElementById('search_input').value = '';
setSearchResults(null);
}
function searchChange(e) {
if (!e.target.value) return setSearchResults(null);
setSearch(e.target.value);
setIsSearching(true);
}
async function updateQuery(query) {
const data = {
search: query
}
const results = await api.search.query(data);
setSearchResults(results);
setIsSearching(false);
}
function selectResult(url) {
props.history.push(url);
setSearchResults(null);
}
function useDebounce(value, delay) {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
}
useEffect(() => {
if (debouncedSearchTerm) {
updateQuery(debouncedSearchTerm);
} else {
setSearchResults(null);
}
}, [user, debouncedSearchTerm])
return (
<ContentContainer style={{boxShadow: '0 0px 0px 0 #000000', position: 'fixed', zIndex: 1000}} backgroundColor='#00121A' borderRadius='0px' width='100%'>
<Section style={{display: 'flex', justifyContent: 'center', alignItems: 'center', height: '50px'}} width='1200px'>
<SearchInput height={'30px'} margin='0px 20px 0px 0px' isSearching={isSearching} selectResult={selectResult} onChange={searchChange} onClear={clearSearch} results={searchResults} />
</Section>
</ContentContainer>
)
}
function mapStateToProps(state) {
return {
user: state.user.data,
notifs: state.notifs
}
}
export default connect(mapStateToProps, { logout, fetchNotifs, updateNotifs })(TopNav);
Tl;DR
Using react-router for site navigation. Doesn't update component if navigating from /teams/111111 to /teams/222222 but does update if navigating from /users/111111 to /teams/222222.
Any and all help appreciated!
When a URL's path changes, the current Component is unmounted and the new component pointed by the new URL is mounted. However, when a URL's param changes, since the old and new URL path points to the same component, no unmount-remount takes place; only the already mounted component receives new props. One can make use of these new props to fetch new data and render updated UI.
Suppose your param id is parameter.
With hooks:
useEffect(() => {
// ... write code to get new data using new prop, also update your state
}, [props.match.params.parameter]);
With class components:
componentDidUpdate(prevProps){
if(this.props.match.params.parameter!== prevProps.match.params.parameter){
// ... write code to get new data using new prop, also update your state
}
}
Use KEY:
Another approach could be to use the unique key prop. Passing a new key will force a
component to remount.
<Route path="/teams/:parameter" render={(props) => (
<Team key={props.match.params.parameter} {...props} />
)} />
Re-render does not cause component to re-mount so use useEffect hook to call initializing logic in your component whenever props changes and update your state in the callback.
useEffect(() => {
//Re initialize your component with new url parameter
}, [props]);
I'm trying to add a chart using recharts with the latest exchange rates of some currencies. Data is shown correctly, but the chart always starts at 0 and goes to a bit above the max value.
The chart is correct, however it doesn't need to start at 0, because doing this, it is almost a line.
Here is the picture of the chart:
I'd like that recharts could calculate automatically the ticks, so it would begin a little bit below the minimum value from the data and finish a little bit above the maximum value.
Here is my code:
import React, { useEffect, useState } from "react";
import { StyledCurrencyChart } from "./styles";
import {
AreaChart,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Area,
ResponsiveContainer
} from "recharts";
import useExchangeRateProvider from "../../hooks/useExchangeRateProvider";
import api from "../../services/api";
import theme from "../../styles/customMuiTheme";
import moment from "moment";
function Chart({ data }) {
return (
<ResponsiveContainer width="100%" height={200}>
<AreaChart
width="100%"
height={250}
data={data}
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
>
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop
offset="5%"
stopColor={theme.palette.secondary.main}
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor={theme.palette.secondary.main}
stopOpacity={0}
/>
</linearGradient>
</defs>
<XAxis
dataKey="date"
tickFormatter={formatDate}
style={{ fill: "#ffffff" }}
/>
<YAxis tickFormatter={formatRate} style={{ fill: "#ffffff" }} />
<CartesianGrid
strokeDasharray="3 3"
fill="rgba(255, 255, 255, 0.3)"
/>
<Tooltip />
<Area
type="monotone"
dataKey="rate"
stroke={theme.palette.secondary.main}
fillOpacity={1}
fill="url(#colorUv)"
/>
</AreaChart>
</ResponsiveContainer>
);
}
// function to format date
function formatDate(tickItem) {
return moment(tickItem).format("MMM Do YY");
}
// function to format rate
function formatRate(tickItem) {
return parseFloat(tickItem).toLocaleString("en-US");
}
export default function CurrencyChart() {
// selected country
const exchangeRateProvider = useExchangeRateProvider();
const country = exchangeRateProvider.state.exchangeRateProvider.country;
// state
const [values, setValues] = useState({
loading: true,
error: false,
data: {}
});
// update chart on country change
useEffect(() => {
async function updateChart() {
try {
const { data } = await api.get(
`/public/rates/history/${country}`
);
setValues({ loading: false, error: false, data });
} catch (e) {
setValues({ loading: false, error: true, data: {} });
}
}
updateChart();
}, [country]);
return (
<StyledCurrencyChart>
<Chart data={values.data} />
</StyledCurrencyChart>
);
}
How can I achieve it? I tried messing around with interval and ticks props under the <YAxis>, but I couldn't make it work.
Thanks in advance
Use the yAxis domain prop:
<YAxis type="number" domain={[0, 1000]}/> // set to whatever you want [yMix, yMax]
For calculating automatically you can use something like these
<YAxis type="number" domain={['dataMin', 'dataMax']} />
<YAxis type="number" domain={[0, 'dataMax']} />
<YAxis type="number" domain={['auto', 'auto']} />
Please make sure to use integer numbers for the values, If you use a string as a value for YAxis it can not recognize the max value correctly.
thats it i need to get the values from the api before this one loads the slider this is how i call the api
useEffect(() => {
async function BCcontroller() {
const vCreationUser = 6;
const vSolicitudeId = 8;
const { data } = await ForceApi.post(`/ConsultBCController.php`, {vSolicitudeId, vCreationUser});
const values = data.terms;
setpterms(data.terms);
//console.log(values);
const [termA, termB, termC, termD] = values.split(',');
setvA(Number(termA));
setvB(Number(termB));
setvC(Number(termC));
setvD(Number(termD));
// console.log(values);
}
BCcontroller();
}, );
this is the slider code
<View style={{ alignItems: "stretch", justifyContent: "center" }}>
<Slider
maximumValue={D > 0 ? 4 : 3}
minimumValue={1}
step={1}
value={valuesFromApi.indexOf(Value)}
onValueChange={index => setValue(valuesFromApi[index])}
/>
<View style={styles.plazos}>
<Text style={styles.plazo1}>{A} meses</Text>
<Text style={styles.plazo2}>{B} meses</Text>
<Text style={styles.plazo3}>{C} meses</Text>
{D > 0 ? <Text style={styles.plazo3}>{D} meses</Text> : null}
</View>
<Text style={styles.slideText}>Su credito por: ${A}MXN</Text>
<Text style={styles.slideText}>Usted recibe: ${A}MXN</Text>
<Text style={styles.slideText}>A un plazo de: {sliderValue2} meses</Text>
<Text style={styles.PaymentText}>Su pago: ${A}.00 MXN</Text>
</View>
i thougt it was this way but the screen loads with a lot of undefineds and then it get the values of the api, so i want to have the values first and then render te components thanks for your help
You probably want your component to return null when there is no data yet. Only when the data is there, you can return the view+Slider.
Something like this:
const MyComponent = () => {
const [data, setDate] = useState();
useEffect(() => {
// ...
const { data } = await ForceApi.post(`/ConsultBCController.php`, {vSolicitudeId, vCreationUser});
setData(data)
// ...
}, [])
if (!data) return null;
return (
<View style={{ alignItems: "stretch", justifyContent: "center" }}>
// ...
</View>
)
}
When data is there, you call setData which will cause a rerender returning the View+Slider.
Of course the code above is incomplete and untested. It's intended to convey my intention. If it doesn't quite make sense, leave a comment and I'll try to enhance.