why is afterSetExtremes function being called twice highcharts using react - reactjs

I want to implement lazy loading functionality as show here.
jsfiddle.net/uf34oecg/
If you check its console, it prints loadInitialData once only at the time of loading the chart for the first time.
Then it never prints it or once you select 1m (after first load), it calls afterSetExtremes function and prints afterSetExtremes.
I found the same example converted to reactjs code
https://codesandbox.io/s/highcharts-react-demo-forked-wvrd6?file=/demo.jsx:673-688
In this example when you load for the first time, it calls afterSetExtremes twice which is wrong. It should not call afterSetExtremes at initial loading.
I don't understand why is that happening? I'm unable to figure it out after spending couple of hours.
Because of it, it behaves badly in real project.

That's because there are different orders of action.
With React:
Chart is created without data
Data is received and the chart is updated
Without React:
Data is received
Chart is created
As a solution, you can call loadInitialData in useEffect hook and render HighchartsReact component after the data has been received.
const ChartComponent = () => {
const [options, setOptions] = useState(null);
useEffect(() => {
loadInitialData();
}, []);
function loadInitialData() {
fetch(dataURL)
.then((res) => res.ok && res.json())
.then((data) => {
...
setOptions({
...chartOptions,
series: [
{
data
}
],
...
});
});
}
function afterSetExtremes(e) {
...
}
return (
options && (
<HighchartsReact
constructorType="stockChart"
highcharts={Highcharts}
options={options}
/>
)
);
};
Live demo: https://codesandbox.io/s/highcharts-react-3tcdw7?file=/demo.jsx

Related

Creating Chart.js using API data in react

I am new to React, I dont really know how React hook works, here I encounter a problem. I tried to draw a chart using Chart.js and re draw it in specific interval using data from API, the problem is the chart does not read the data. This is the get data function:
const getData = async ()=>{
const response = await axiosJWT.get('http://localhost:5000/in-total-assets', {
headers:{
Authorization: `Bearer ${token}`
}
})
var formated = [];
formated.push = response.data[0].Total_OR_Weight;
formated.push = response.data[0].Total_IN_Weight;
setData(formated)
}
And this is the chart renderer function:
async function getChart(){
await getData();
let config = {
type: "doughnut",
data: {
labels: [
"Organic",
"Inorganic",
],
datasets: [
{
label: 'Dataset',
backgroundColor: ["#ed64a6", "#4c51bf"],
data: data,
hoverOffset: 5
},
],
}
};
let ctx = document.getElementById("doughnut-chart").getContext("2d");
window.myBar = new Chart(ctx, config);
}
And here is the useEffect function:
React.useEffect(() => {
refreshToken();
getChart();
const id = setInterval(getChart, 10000)
return ()=> clearInterval(id);
}, []);
The result is the chart show undefined as the value of the chart, any idea how to solve it?
Your html file in the part where you are going to draw the chart should look something like this:
<div class="canvasContainer" style="margin-top:20px; margin-bottom:20px;">
<canvas id="doughnut-chart"></canvas>
</div>
The first think that come to my mind is if there is any data at all coming from your getData function, if data is flowing in then my next guess is that setData is not updating it in time by the time you decide to use the variable data in your code.
I found this on a google search
State updates are asynchronous. This was true in class-based components. It's true with functions/Hooks
And
Even though they are asynchronous, the useState and setState functions do not return promises. Therefore we cannot attach a then handler to it or use async/await to get the updated state values
So, there it lays your problem. You need to redefine the part where you fetch the data and the part where you set your chart up, since the await part is not functioning like you think it is for hook functions. Something like this.
useEffect(() => {
getData() // it handles the setting up the data variable part
}, []); // If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument.
useEffect(() => {
refreshToken();
getChart(); // it only sets the chart up, nothing else
}, [data]); // our component re-renders with every change on the var data
You can have as many useEffect blocks as you like and you can get rid off the part getContext("2d") from your ctx.

React useState hook not updating with axios call

I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.
I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.
Here is some of the code I have tried.
The data i am hoping to get
[
{id:1},
{id:2},
{id:3},
{id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'
const [carouselTitle, setCarouselTitle] = useState([])
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await axios('api').then(
response => {
console.log(response.data)
setCarouselTitle(response.data)
console.log(carouselTitle)
})
};
return(
<h1>{carouselTitle[1].id}</h1>
)
console logging the data works fine but when i console log the state it does not work.
2ND METHOD I TRIED
useEffect(() => {
const fetchData = async () => {
const res = await axios('api');
const carouselTitleAlt = await res.data;
setCarouselTitle({ carouselTitleAlt });
console.log(carouselTitleAlt);
console.log(carouselTitle);
};
fetchData();
}, []);
Again console logging the const inside the useEffect displays the correct information but logging the state does not work.
Appreciate your responses or better ways of displaying the data.
setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously
It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.
To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :
useEffect(() => {
console.log(carouselTitle);
}, [carouselTitle]);
That said, your component should behave correctly, it will be refreshed when the state is updated.
In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :
{carouselTitle && <H1>{carouselTitle[0].id}}
https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper
First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:
return(
<h1>{carouselTitle[1].id}</h1>
)
Because component returns first item of an array that has nothing. I prefer to you do it like this:
return(
<h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)
And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.
So state data doesn't show immediately after setting that.
You should trigger useEffect for run fetch function
useEffect(()=>{fetchData();},[carouselTitle])

My react component never displays the information from the database

I have a small web app displays game information.
I am using React hooks so that the component is modern.
When this component loads, I want it to connect to the api via axios, and get the description of the game.
But when it loads, the value inside the <GameVault /> is always null.
When I look in the database, it is not null. If I hit the api directly, it does return the game description.
My console.log is hit twice for some reason. The first time it's null, the second time it has the needed value.
I am also not getting any errors, so I don't know why this isn't working.
Here is the code:
const Vault = ({ game }) => {
const [gameText, setGameText] = useState(null);
async function fetchGameText() {
const response = await axios.get(`/api/gamermag/${game.id}/gameDescriptionText`);
setGameText(response.data);
}
useEffect(() => {
fetchGameText();
}, []);
console.log("gameText: ", gameText);
const gamerValue = useMemo(() => {
return gameText ? gameText : "";
}, [gameText]);
return (
<GameVault value={gamerValue} />
)
}
export default Vault;
Is there a way to get this to work?
Thanks!
You need to wait for the data to load from the server. While the data is being fetched, gameText will be null and when it's done fetching, it stores the response. That is why your console.log hit twice. The first time is the component's first render, and the second time is when the gameText changes its state which caused a rerender.
You need to add logic to wait for the data.
if(!gameText){
return <div>loading...</div>
}

React testing library - fakeTimers with waitFor/waitForElementToBeRemoved

Last time I have updated testing-library/dom from version 7.29.4 to 8.0.0. After that tests which have jest.useFakeTimers stopped working whenever waitFor/waitForElementToBeRemoved is used.
export default function Test() {
const [loaded, setLoaded] = useState(false);
const getDataCallback = useCallback(() => {
return getData();
}, [])
useEffect(() => {
getDataCallback().then(data => {
setLoaded(true)
});
}, [])
return (
<>
{
loaded ?
<>
{new Date().toDateString()} //displays current date
</>
: <Loader/>
}
</>
)}
Test code:
const mockFunc = jest.spyOn(api, "getData");
const fakeData = [{ date: "2020-01"}, { date: "2020-02"},];
beforeEach(() => {
jest.useFakeTimers("modern").setSystemTime(new Date(2020, 2, 3));
mockFunc.mockResolvedValue(fakeData);
})
it("test", async () => {
render(<Test />);
await waitForElementToBeRemoved(screen.queryByTestId("loader"));
expect(screen.getByText(/tue mar 03 2020/i)).toBeInTheDocument();
})
In this code it's some fake api call, when it's done then we want to display the current date. If the call is not finished, then some loader/spinner is on the screen. When I remove loader state and waitForElementToBeRemoved() from code I have mocked date on the screen and everything works like expected, otherwise real date is displayed.
I'm not sure what is happening inside of your getData, but if it is using setTimeout or similar, then you need to tell jest to advance the fake timers or flush them.
I had a similar issue where I was using real timers and all tests passed, then when using fake timers they all failed. In my scenario I think it was because my tests were not waiting for the timeout to finish and just immediately executed assertions as if timeouts had passed when they really hadn't. Adding jest.advanceTimersByTime(theSetTimeoutTime) before the calls to waitForElementToBeRemoved fixed my tests in almost all cases.

how to load a function when a page load react native

I'm using react-native with hooks, and I'm trying to load a JSON from AsyncStorage every time a user opens one of my react-native screens This JSON contains information on what my states should be set to.
How can I call a function that runs every time this screen is opened?
i know that without hooks this should be done with useEffect, but when i put my api call there it makes an error
this is my code
useEffect(() => {
const getKind = () => {
ForceApi.post(`/GetKindPensionController.php`)
.then(res => {
setpPensionKind(res.data.pension);
})
}
}, []);
You are missing call the getKind, and it should be a async function! For a better code try something like:
useEffect(() => {
async function getKind() {
const { data } = await ForceApi.post(`/GetKindPensionController.php`)
setpPensionKind(data.pension);
}
getKind();
}, []);

Resources