React and react-chartjs-2, Line chart not displaying data from API - javascript

I am trying to render a Line chart using react-chartjs-2. Right now it works fine when I pass in static props to the Line Component. But, when I fetch data from an API, set it to a variable, then pass that variable as the prop. My Line Component is not re-rendering with the new data.
I am logging the props being passed into the Line Component and I can see it first arrives as null and then I receive the good data from the API. So it looks like the Line Component is not re-rendering after receiving the props? I am probably doing this wrong. Please help.
import React from "react";
import { Line } from "react-chartjs-2";
export default class ExpenseChart extends React.Component {
constructor(props) {
super(props);
this.state = {
marketData: [100, 200, 300],
chartData: {
labels: this.props.monthNames,
datasets: [
{
backgroundColor: "rgba(142, 243, 197, 0.5)",
pointBackgroundColor: "#fff",
pointHoverBackgroundColor: '#fff',
pointStyle: "circle",
label: "Monthly Expenses",
borderColor: "#2be1d8",
borderWidth: 3,
borderJoinStyle: "round",
lineTension: 0.3,
fontColor: "#fff",
hitRadius: 5,
hoverRadius: 8,
radius: 4,
data: this.props.monthExpenses
},
],
},
};
}
render() {
console.log("why no names", this.props.monthNames)
return (
<div className="expenseChart">
<h2 className="expenseChart__name">{this.props.graphname}</h2>
<Line
data={this.state.chartData}
options={{
maintainAspectRatio: false,
responsive: true,
aspectRatio: 3,
scales: {
yAxes: [
{
ticks: {
display: false,
},
},
],
},
layout: {
padding: {
right: 10,
},
},
}}
/>
</div>
);
}
}
And then the parent component is connected to a redux store and it looks like this:
import React from "react";
import { connect } from 'react-redux';
import ExpenseChart from "../elements/ExpenseChart";
import { fetchExpenses } from '../../actions/index';
class Dashboard extends React.Component {
componentDidMount() {
this.props.dispatch(fetchExpenses());
}
render() {
let labels = this.props.months && this.props.months;
return (
<main className="dashboard">
<ExpenseChart
monthNames={labels}
monthExpenses={["123", "123", "12312", "12341", "231231", "1231", "1231"]}
// I am receiving monthExpenses props into the ExpenseChart component
// but not monthNames
/>
</main>
);
}
}
const mapStateToProps = state => ({
auth: state.app.auth,
months: state.app.months,
});
export default connect(mapStateToProps)(Dashboard);
I've done something similar before, fetching data from an API and passing it as props to the Line Component. But only difference is I am using redux here. And obviously, this time the Line Component is not receiving the good data.

The issue might be on this componentDidMount code.
componentDidMount() {
this.props.dispatch(fetchExpenses());
}
As per docs on dispatch
action (Object†): A plain object describing the change that makes
sense for your application. Actions are the only way to get data into
the store, so any data, whether from the UI events, network callbacks,
or other sources such as WebSockets needs to eventually be dispatched
as actions. Actions must have a type field that indicates the type of
action being performed. Types can be defined as constants and imported
from another module. It's better to use strings for type than Symbols
because strings are serializable. Other than type, the structure of an
action object is really up to you. If you're interested, check out
Flux Standard Action for recommendations on how actions could be
constructed.
Here fetchExpenses might be returning a promise and in that case you might need
fetchExpenses().then(apiRes => dipatch({type: "ACTION_TYPE": payload: apiRes}))
Another approach can be to use redux-thunk

Ok I solved this just by checking if the data I was receiving was not null before passing it as props. Which is what I thought this line would do let labels = this.props.months && this.props.months;
<main className="dashboard">
{ this.props.months != null ? <ExpenseChart monthNames={labels}/> : ''; }
</main>

Related

Next.js: window is not defined

I'm trying to use apexcharts for a next.js application and it's returning me window is not defined.
I would love any help with that.
Does someone know what is happening and why?
import React from 'react';
import Chart from 'react-apexcharts';
export default class Graficos extends React.Component <{}, { options: any, series: any }> {
constructor(props:any) {
super(props);
this.state = {
options: {
chart: {
id: "basic-bar"
},
xaxis: {
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999]
}
},
series: [
{
name: "series-1",
data: [30, 40, 45, 50, 49, 60, 70, 91]
}
]
};
}
render() {
return (
<div className="row">
<h1>Gráfico Básico</h1>
<div className="mixed-chart">
<Chart
options={this.state.options}
series={this.state.series}
type="bar"
width={500}
/>
</div>
</div>
);
}
}
One of Next.js's key features is that it can render parts of your React application on the server or even at build time. While this can be helpful in improving your page's performance, the downside is that the server does not provide the all same APIs that your application would have access to in the browser. In this case, there is no global window object defined.
Unfortunately, searching the source code for apexcharts.js turns up many references to window: https://github.com/apexcharts/apexcharts.js/search?q=window. This also occurs in their React wrapper: https://github.com/apexcharts/react-apexcharts/blob/ecf67949df058e15db2bf244e8aa30d78fc8ee47/src/react-apexcharts.jsx#L5. While there doesn't seem to be a way to get apexcharts to avoid references to window, you can prevent Next.js from using the chart on the server. The simplest way to do that is to wrap any reference to the code with a check for whether window is defined, e.g.
<div className="mixed-chart">
{(typeof window !== 'undefined') &&
<Chart
options={this.state.options}
series={this.state.series}
type="bar"
width={500}
/>
}
</div>
With apexcharts, you will also need to do this for the component import because the import alone will trigger a reference to window as shown in that second link. In order to get around that problem you will need to use a dynamic import as opposed to the normal import you currently have: https://nextjs.org/docs/advanced-features/dynamic-import
import dynamic from 'next/dynamic'
const Chart = dynamic(() => import('react-apexcharts'), { ssr: false });
if (typeof window !== "undefined") {
host = window.location.host;
console.log("host--------------", host);
}

declaration merging for react-native-elements theme

I am using react-native-elements in my react-native application.
My app is wrapped with the ThemeProvider to pass the theme down to all components.
<SafeAreaProvider>
<ThemeProvider theme={Theme}>
<Loader visible={loader.loading} text={loader.message} absolute={true} />
<RootNavigation />
</ThemeProvider>
</SafeAreaProvider>
In the theme file i define the values i want to use across the app.
const theme = {
colors: {
primary: '#6A69E2',
primaryDark: '#4747c2',
primaryLight: 'rgba(106, 105, 226, 0.35)',
gray: {
dark: '#242424',
default: '#666',
medium: '#999',
light: '#ccc',
lightest: '#e7e7e7',
},
},
text: {
size: {
small: 12,
default: 16,
large: 18,
h1: 26,
h2: 22,
h3: 20,
},
},
Text: {
style: {
fontSize: 16,
color: '#242424',
fontFamily: 'Roboto',
},
},
Button: {
style: {
borderRadius: 50,
},
disabledStyle: {
backgroundColor: 'rgba(106, 105, 226, 0.35)',
},
},
};
export default theme;
For the values the original theme of react-native-elements providing this is working. For example i can access the colors by using
const theme = useTheme()
theme.colors.primary
But when i want to add some new properties like primaryDark i'll get an linter error.
Object literal may only specify known properties, and 'primaryDark' does not exist in type 'RecursivePartial<Colors>'.ts(2322)
In the doc of react-native-elements is a part about declaration merging, but i don't understand how i can archive this
https://reactnativeelements.com/docs/customization/#typescript-definitions-extending-the-default-theme.
Somebody could help me with this?
Well, declaration merging still works. This seems like a bug on the lib's part.
Their doc says you can augment the Color interface in module 'react-native-elements'. But currently (as of 2021-04-18, with v3.3.2) that interface is actually hidden inside module 'react-native-elements/dist/config/colors', not directly exposed at the top level, weird.
I suggest you file an issue to their repo. Never mind, someone already filed the issue.
Tested on my machine, following solution works.
import React from 'react'
import { useTheme, ThemeProvider } from 'react-native-elements'
declare module 'react-native-elements/dist/config/colors' {
export interface Colors {
primaryDark: string
primaryLight: string
}
}
const ChildComp = () => {
const theme = useTheme()
theme.theme.colors.primaryDark // <-- No more error 🎉
return <div>foobar</div>
}
Reply to OP's comment. You can augment interface however you like, as long as the augmented key doesn't exist before. For example add foobar key to FullTheme.
declare module 'react-native-elements' {
export interface FullTheme {
foobar: string
}
}

Parsing JSON Data to Chartjs in React

I have been import JSON files from my MongoDB server through axios. I am able to fetch the data successfully but showing it on chart is not possible. I ahve seen other answers and it seems easy to store each key column in a seperate variable and then loading to labels and data objects, but this more optimized and Chartjs also allows us this approach as listed in its documentation but I might be going wrong somewhere. In need for help as I need to implement it for my project
import React, { useState } from 'react';
import axios from 'axios';
import { useEffect } from 'react';
import {Bar, Line} from 'react-chartjs-2';
const URL = process.env.REACT_APP_SERVER_URL || 'http://localhost:5000/';
function ChartRoughPage(props) {
const [historicalData,setHistoricalData] = useState([]);
useEffect(()=>{
axios.get(URL+'stock/BLUEDART')
.then((response)=>{
if(response.status===200){
console.log(response.data)
setHistoricalData(response.data)
}
else{
console.log("ERROR: "+response.status+" , "+response.statusText)
}
})
.catch((err) => console.log.err);
},[]);
return (
<div>
<Line
data={{
datasets:[{
label:'Everyday Chart',
data : historicalData,
parsing:{
xAxisKey:'DATE',
yAxisKey:'CLOSE'
}
}]
}}
/>
</div>
);
}
export default ChartRoughPage;
Output : It just shows a chart with no data
For better understanding here is the link to the documentation:
https://www.chartjs.org/docs/latest/general/data-structures.html
Also I have tried following things on my code:
Tried writing it to options
...
<Line
data={{
datasets:[{
data : historicalData
}]
}}
options={{
parsing:{
xAxisKey:'Date',
yAxisKey:'Close'
}
}}
/>
...
Providing a static data like:
historicalData = [{Date : '22-02-2000',Close: 56},{Date : '22-03-2000',Close: 656},{Date : '23-05-2000',Close: 6}]
also the documents that I send as JSON from MongoDB is like this is(all the values are accessible by their keys):
{"_id":{"$oid":"some-object-id"},"Date":"2019-01-03","Symbol":"20MICRONS","Series":"EQ","Prev Close":{"$numberDouble":"44.05"},"Open":{"$numberDouble":"44.05"},"High":{"$numberDouble":"44.1"},"Low":{"$numberDouble":"43.1"},"Last":{"$numberDouble":"43.4"},"Close":{"$numberDouble":"43.45"},"VWAP":{"$numberDouble":"43.48"},"Volume":{"$numberInt":"15741"},"Turnover":{"$numberDouble":"68447485000.0"},"Trades":{"$numberDouble":"368.0"},"Deliverable Volume":{"$numberInt":"9487"},"%Deliverble":{"$numberDouble":"0.6027"}}
Will be grateful for your help!
It seems the issue is in the type of the x-axis, cause if you provide
datasets: [{
data: [{Date : '11',Close: 56},{Date : '22',Close: 656},{Date : '23',Close: 6}],
showLine: true,
fill: false,
borderWidth: 1,
parsing: {
xAxisKey: 'Date',
yAxisKey: 'Close'
},
backgroundColor: 'rgba(255, 99, 132, 1)'
}]
it draws just fine
So you should better find how to show time on the x-axis
I know I am late, but I have to answer this)

Why is vue-chartjs not showing my passed data?

working on a covid app to get familiar with vue2js.
Am now trying to get a graph with vue-chartjs but am failing to pass the data to the graph/chart component.
I make an API request with vuex and passing the data to my component: CountryGraph.vue which contains a Graph.vue with the chart itself.
vuex -> CountryGraph.vue -> Graph.vue
Passing data into CountryGraph.vue works:
But when I try to pass my data (countryGraph) as props to my char/Graph.vue component, then it is not done and I get in Graph.vue only the value undefined:
Why?
Below my code, first the CountryGraph.vue:
<template>
<section class="countryGraph">
<LineChart
:chartdata="chartData"
:options="chartOptions"
/>
</section>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import LineChart from "../graph/Graph";
export default {
name: "CountryGraph",
components: { LineChart },
data: () => ({
chartData: {
labels: this.countryGraph.map((el) => el.date),
datasets: [
{
label: "Confirmed",
backgroundColor: "#f87979",
data: this.countryGraph.map(
(el) => el.confirmed
),
},
],
},
chartOptions: {
responsive: true,
maintainAspectRatio: false,
},
}),
methods: {
...mapActions(["selectCountryGraph"]),
},
computed: {
...mapGetters(["countryGraph"]),
},
};
</script>
<style></style>
And my chart/Graph.vue component which is made so, that I can reuse it (as stated in vue-chartjs guide):
<script>
import { Bar } from "vue-chartjs";
export default {
extends: Bar,
props: {
chartdata: {
type: Object,
default: null,
},
options: {
type: Object,
default: null,
},
},
mounted() {
this.renderChart(this.chartdata, this.options);
},
};
</script>
<style />
When I use mocked data, like instead of
labels: this.countryGraph.map((el) => el.data)
I do labels: ["q", "w", "e", "r", "t"]
and instead of
data: this.countryGraph.map(el => el.confirmed)
I do data: [0, 1, 2, 3, 4]
everything works fine.
Also, when I pass my variables directly into the component, like:
<LineChart
:chartdata="this.countryGraph.map((el) => el.data)"
:options="chartOptions"
/>
Then I can see the data as props in the child (Graph.vue) component.
But in this case I use v-bind: and in the earlier one not. Maybe that is the problem?
A couple issues to note:
It looks like you're mapping a nonexisting property (el.data should be el.date). Possibly just a typo in the question.
this.countryGraph.map((el) => el.data) ❌
^
data() is not reactive, and cannot rely on computed props, so the countryGraph computed prop will not be available in data() and will not update chartData with changes. One way to fix this is to make chartData a computed prop:
export default {
computed: {
...mapGetters(["countryGraph"]),
// don't use an arrow function here, as we need access to component instance (i.e., this.countryGraph)
chartData() {
return {
labels: this.countryGraph.map((el) => el.date),
datasets: [
{
label: "Confirmed",
backgroundColor: "#f87979",
data: this.countryGraph.map((el) => el.confirmed),
},
],
}
}
}
}

How to use vanilla javascript inside Reactjs component?

I am using chartist.js and I am using the chartist within reactjs component.
I am referring this http://gionkunz.github.io/chartist-js/examples.html#simple-pie-chart
chartist.js:
var Chartist = {
version:'0.9.5'
}
(function (window, document, Chartist) {
var options = {
labelInterpolationFnc: function(value) {
return value[0]
}
};
var responsiveOptions = [
['screen and (min-width: 640px)', {
chartPadding: 30,
labelOffset: 100,
labelDirection: 'explode',
labelInterpolationFnc: function(value) {
return value;
}
}],
['screen and (min-width: 1024px)', {
labelOffset: 80,
chartPadding: 20
}]
];
})();
Reactjs component:
import React, { Component } from 'react';
var data = {
labels: ['Bananas', 'Apples', 'Grapes'],
series: [20, 15, 40]
};
showPieChart(data){
new Chartist.Pie('.ct-chart', data, options, responsiveOptions);
}
class Chart extends Component {
render(){
return(
<div>
<div className="center">
{showPieChart}
</div>
</div>
)}
}
export default Chart;
Nothing is displayed on web page. How can I access vanilla javascript inside react component.
Your question is a little bit misleading, and can be interpreted in two ways.
#1. If you're asking how to integrate Chartist library with React, here's how you can do it:
There's a wrapper library, that already did it for us: https://www.npmjs.com/package/react-chartist
You can use it as follow (example taken from their repo):
import React from 'react';
import ReactDOM from 'react-dom';
import ChartistGraph from 'react-chartist';
class Pie extends React.Component {
render() {
var data = {
labels: ['W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'W7', 'W8', 'W9', 'W10'],
series: [
[1, 2, 4, 8, 6, -2, -1, -4, -6, -2]
]
};
var options = {
high: 10,
low: -10,
axisX: {
labelInterpolationFnc: function(value, index) {
return index % 2 === 0 ? value : null;
}
}
};
var type = 'Bar'
return (
<div>
<ChartistGraph data={data} options={options} type={type} />
</div>
)
}
}
ReactDOM.render(<Pie />, document.body)
#2. If you generally asking how to integrate other libraries into React, then I recommend you to check the official React docs, because there's a really good tutorial about the topic - Integrating with Other Libraries
So, if you don't want to use the wrapper library (react-chartist), then you can check its main component too. It's a great starting point (that follows React recommendations) to understand how to create your own wrapper: https://github.com/fraserxu/react-chartist/blob/master/index.js

Categories