React Native Highcharts call method - javascript

Hi I am using react native highcharts for chart. Everything is working fine. But I can't able to call a react method within the highcharts declaration.
Here is my component.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading:false
};
this.getSingleData = this.getSingleData.bind(this);
}
getSingleData(x, y) {
console.log(x, y)
}
render() {
var Highcharts='Highcharts';
const configData = {
chart: {
type: 'areaspline',
marginTop:80
},
plotOptions: {
series: {
point: {
cursor: 'pointer',
events: {
click: () => {
this.getSingleData(this.x, this.y);
}
}
}
}
},
series: [{
showInLegend: false,
name: "Total",
data: []
}]
}
return (
<ChartView style={{height:300}} config={configData}></ChartView>
);
}
}
When I try to call getSingleData the method is not calling.
It was works when using reactjs. But it is not working in React Native. Because I am declaring the click event inside render. I don't know how to call a class method.
I've tried with static method, but no luck.
Please give me some suggestions.

Here is the code for your issue, it worked for me. Hopefully, it will work for you too.
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading:false
};
this.getSingleData = this.getSingleData.bind(this);
}
getSingleData(x, y) {
console.log(x, y)
}
onMessage = (message)=>{
/*
----
Write your condition in this function on which basis you want to render the content by clicking on your graph
----
*/
}
render() {
var Highcharts='Highcharts';
const configData = {
chart: {
type: 'areaspline',
marginTop:80
},
plotOptions: {
useHTML:true
series: {
point: {
cursor: 'pointer',
events: {
click: () => {
window.ReactNativeWebView.postMessage(this.y);
this.getSingleData(this.x, this.y);
}
}
}
}
},
series: [{
showInLegend: false,
name: "Total",
data: []
}]
}
return (
<ChartView style={{height:300}} config={configData} onMessage={m}></ChartView>
);
}
}

Related

Call a parent component function from a child component

Fairly new to React. I have a lottery wheel as part of a hobby project website:
The Wheel object was downloaded with npm:
npm install lottery-wheel
import Wheel from 'lotter-wheel'
class Lottery extends Component {
constructor() {
bla bla
}
componentDidMount() {
new Wheel( {
el: document.querySelector("#wheel"),
onSuccess(data) {
alert(`Congratulations, you picked up ${data.text}`)
/* I want to pass the data here to Parent */
},
onButtonHover(anime, button) {
anime({
targets: button,
scale: 1.3,
perspective: 80,
duration: 400
});
},
});
}
render() {
return (
<div id="wheel"></div>
)
}
}
SO, In the callback-function 'onSuccess' I want to pass the 'data' from the Wheel child component to the 'Lottery' parent component.
How can I do this? I know how props work but not in this case.. Can I use a hook, in that case, how?
I want avoid downloading and going into 'Wheel' definition since it was not created by me.
Define a function and set as the onSuccess callback in the Wheel.
class Lottery extends Component {
successHandler = data => {
alert(`Congratulations, you picked up ${data.text}`);
};
componentDidMount() {
new Wheel({
el: document.querySelector("#wheel"),
data: [{
text: 'apple',
chance: 20
}, {
text: 'banana'
}, {
text: 'orange'
}, {
text: 'peach'
}],
onSuccess: this.successHandler,
onButtonHover(anime, button) {
anime({
targets: button,
scale: 1.3,
perspective: 80,
duration: 400
});
}
});
}
render() {
return <div id="wheel"></div>;
}
}

vue-chart.js / vuex: Chart not updating when vuex state changes

I have created a doughnut chart using vue-chart.js, Chart.js and using some values which are within my vuex state. The chart works apart from when the vuex state is updated the chart doesn't updated too.
I have tried to use computed properties to try keep the chart up to date. But then I get the following errors:
Error in callback for watcher "chartData": "TypeError: Cannot set property 'data' of undefined"
TypeError: Cannot set property 'data' of undefined
DoughnutChart.vue:
<script>
import { Doughnut, mixins } from 'vue-chartjs';
const { reactiveProp } = mixins;
export default {
extends: Doughnut,
mixins: [reactiveProp],
props: ['chartData', 'options'],
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
</script>
AppCharts.vue:
<template>
<div id="chart_section">
<h2 class="section_heading">Charts</h2>
<div id="charts">
<DoughnutChart :chart-data="datacollection" :options="chartOptions" class="chart"></DoughnutChart>
</div>
</div>
</template>
<script>
import DoughnutChart from './DoughnutChart';
import { mapGetters } from 'vuex';
export default {
components: {
DoughnutChart
},
computed: {
...mapGetters(['boardColumnData']),
datacollection() {
return {
datasets: [{
data: [this.boardColumnData[0].total.$numberDecimal, this.boardColumnData[1].total.$numberDecimal, this.boardColumnData[2].total.$numberDecimal, this.boardColumnData[3].total.$numberDecimal],
backgroundColor: [
'#83dd1a',
'#d5d814',
'#fdab2f',
'#1ad4dd'
],
borderColor: [
'#83dd1a',
'#d5d814',
'#fdab2f',
'#1ad4dd'
],
}]
}
},
},
data() {
return {
chartOptions: null
}
},
mounted () {
this.fillData();
},
methods: {
fillData() {
this.chartOptions = {
responsive: true,
maintainAspectRatio: false
}
}
}
}
</script>
this is what boardColumnData looks like after getting the state:
[
{
"name":"Opportunities",
"percentage":{
"$numberDecimal":"0"
},
"total":{
"$numberDecimal":70269
}
},
{
"name":"Prospects",
"percentage":{
"$numberDecimal":"0.25"
},
"total":{
"$numberDecimal":0
}
},
{
"name":"Proposals",
"percentage":{
"$numberDecimal":"0.5"
},
"total":{
"$numberDecimal":5376
}
},
{
"name":"Presentations",
"percentage":{
"$numberDecimal":"0.75"
},
"total":{
"$numberDecimal":21480
}
},
]
The $numberDecimal value is what is updated inside vuex when an event on another component happens. When these values are changed I want the chart to update with the new values.
You must pass the data in the prop. I.e. fastest solution for you would be to have fillData() return the datacollection in the correct format.
E.g.
fillData(){
return {
datasets: [
{
data: [
this.boardColumnData[0].total.$numberDecimal,
this.boardColumnData[1].total.$numberDecimal,
this.boardColumnData[2].total.$numberDecimal,
this.boardColumnData[3].total.$numberDecimal,
],
backgroundColor: ["#83dd1a", "#d5d814", "#fdab2f", "#1ad4dd"],
borderColor: ["#83dd1a", "#d5d814", "#fdab2f", "#1ad4dd"],
},
];
}
}
You need to do the same for the options, but pass them in a new options=yourOptions() prop.
This drove me absolutely bonkers. I hope this still helps you (or someone else).

How To handle click events of different charts on the same page

I want to call a method to handle click event from this vue file:
<template>
<Chart
type="bar"
:labels="labels"
:datasets="datasets"
:options="options"
></Chart>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import Chart from '#/components/Chart.vue';
import { colors } from '#/constants';
#Component({
components: {
Chart
},
})
export default class AlarmsChart extends Vue {
#Prop()
public stats!: any[];
...
get options() {
const options = {
maintainAspectRatio: false,
scales: {
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Number of alarms',
},
},
],
xAxes: [
{
type: 'time',
time: {
parser: 'YYYY-MM-DD',
displayFormats: {
day: 'DD/MM/YY',
},
},
scaleLabel: {
display: true,
labelString: 'Date',
},
},
],
},
legend: {
labels: {
fontFamily: 'Overpass',
},
},
'onClick' : (event, item) => {
if ( item.length == 0 ) return; // Clicked outside any bar.
clickHandler(event);
},
};
return options;
}
}
</script>
To this one:
<template>
<div class="chart-container">
<canvas ref="chart"></canvas>
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue, Watch } from 'vue-property-decorator';
import Chart from 'chart.js';
/**
* The Chart component acts as a wrapper around Chart.js.
*
* This component has various properties that will be passed onto Chart.js.
* This component should be as generic as possible to allow different kinds of
* charts with different kinds of datasets in order to encourage reuse of this
* component without having to deal with tedious workarounds.
*/
#Component
export default class ChartComponent extends Vue {
#Prop()
public datasets!: any;
#Prop()
public labels!: string[];
#Prop()
public options!: any;
#Prop()
public type!: string;
private chart?: Chart;
public mounted() {
// Since we are updating the UI ourselves, always create the chart
// *after* Vue has finished rendering the DOM.
const chartElement = this.$refs.chart as HTMLElement;
this.chart = new Chart(chartElement, {
type: this.type,
data: {
labels: this.labels,
datasets: this.datasets,
},
options: this.options,
});
}
public clickHandler(evt) {
var firstPoint = this.chart.getElementAtEvent(evt)[0];
if (firstPoint) {
var label = this.chart.data.labels[firstPoint._index];
var value = this.chart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
}
}
/** Automatically update Chart.js upon dataset changes. */
#Watch('datasets', { deep: true })
private onChange() {
this.chart.data.labels = this.labels;
this.chart.data.datasets = this.datasets;
this.chart.update();
}
}
</script>
<style lang="scss" scoped>
.chart-container {
position: relative;
height: 100%;
width: 100%;
}
</style>
In the first file in options there is a method call inside the onClick event to: clickHandler(event), I want to call to the method that is in the second file. But no idea how to do it, is it possible?
I want to do this to get data from the bar clicked. So the second file is like a common chart, and the first one is a custom. Several files like the first one use the second file.

How to use highcharts in reactjs with fetched data from API

I want to display data from API with highcharts but still get an error because highchart config declared outside the class app and data fetched inside class app.
It works if use static data,
here is the example from static data link.
import React, { Component } from 'react';
import { render } from 'react-dom';
import ReactHighstock from 'react-highcharts/ReactHighstock';
import './style.css';
const data = [
[1220832000000, 22.56], [1220918400000, 21.67], [1221004800000,
21.66], [1221091200000, 21.81], [1221177600000, 21.28],
[1221436800000, 20.05], [1221523200000, 19.98], [1221609600000,
18.26], [1221696000000, 19.16], [1221782400000, 20.13],
[1222041600000, 18.72], [1222128000000, 18.12], [1222214400000,
18.39], [1222300800000, 18.85], [1222387200000, 18.32],
[1222646400000, 15.04], [1222732800000, 16.24], [1222819200000,
15.59], [1222905600000, 14.3], [1222992000000, 13.87],
[1223251200000, 14.02], [1223337600000, 12.74], [1223424000000,
12.83], [1223510400000, 12.68], [1223596800000, 13.8],
[1223856000000, 15.75], [1223942400000, 14.87], [1224028800000,
13.99], [1224115200000, 14.56], [1224201600000, 13.91],
[1224460800000, 14.06], [1224547200000, 13.07], [1224633600000,
13.84], [1224720000000, 14.03], [1224806400000, 13.77],
[1225065600000, 13.16], [1225152000000, 14.27], [1225238400000,
14.94], [1225324800000, 15.86], [1225411200000, 15.37],
[1225670400000, 15.28], [1225756800000, 15.86], [1225843200000,
14.76], [1225929600000, 14.16], [1226016000000, 14.03],
[1226275200000, 13.7], [1226361600000, 13.54], [1226448000000,
12.87], [1226534400000, 13.78], [1226620800000, 12.89],
[1226880000000, 12.59], [1226966400000, 12.84], [1227052800000,
12.33], [1227139200000, 11.5], [1227225600000, 11.8],
[1227484800000, 13.28], [1227571200000, 12.97], [1227657600000,
13.57], [1227830400000, 13.24], [1228089600000, 12.7],
[1228176000000, 13.21], [1228262400000, 13.7], [1228348800000,
13.06], [1228435200000, 13.43], [1228694400000, 14.25],
[1228780800000, 14.29], [1228867200000, 14.03], [1228953600000,
13.57], [1229040000000, 14.04], [1229299200000, 13.54]
];
const config = {
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
};
class App extends Component {
render() {
return (
<div className="App" />
);
}
}
render(<ReactHighstock config={config}/>,
document.getElementById('root'));
With example above, two variable data and config placed outside the class app, so i think it's not problem.
But if i want to fetch data from API, the data placed inside the class app and config still outside class app.
So i get an error 'data is not defined'
here is the link
import React, { Component } from 'react';
import { render } from 'react-dom';
import ReactHighstock from 'react-highcharts/ReactHighstock';
import './style.css';
const config = {
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
};
class App extends Component {
constructor() {
super();
this.state = {
dataResults: [],
}
}
componentDidMount() {
this.getData();
}
getData = () => {
fetch('https://cdn.rawgit.com/highcharts/highcharts/057b672172ccc6c08fe7dbb27fc17ebca3f5b770/samples/data/usdeur.json')
.then(res => res.json())
.then(data => {
this.setState({
dataResults: data
});
});
}
render() {
return (
<div className="App" />
);
}
}
render(<ReactHighstock config={config}/>,
document.getElementById('root'));
How to use highcharts with fetched data from API?
Your App component is currently not part of your DOM as you're rendering ReactHighstock as a root component. One solution would be to render the chart inside the App component and pass the data dynamically:
const getConfig = data => ({
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
class App extends Component {
constructor() {
super();
this.state = {
dataResults: [],
}
}
componentDidMount() {
this.getData();
}
getData = () => {
fetch('your_endpoint_url')
.then(res => res.json())
.then(data => {
this.setState({
dataResults: data
});
});
}
render() {
const { dataResults } = this.state;
const chartConfig = getConfig(dataResults);
return (
<ReactHighstock config={chartConfig}/>
);
}
}
render(<App/>,
document.getElementById('root'));
You should of course handle a use case where the data has not been fetched yet and display a placeholder.

React - search component to filter array

Recently, I have been searching through to understand the development behind a live search input, the content being an array. In my case, the array is being used to create a file tree. All the code is here: https://codesandbox.io/s/815p3k3vkj
After some investigation and looking through some working examples, the solution seemed simple, but then I understood that I was still confused about it's creation. So, I defined the initial state, but I'm confused of what to do next, to connect the search with the array.
So, I started to do something like this:
export class SearchEngine extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ""
};
this.inputChange = this.inputChange.bind(this);
}
inputChange(e) {
const content = e.target.value;
this.props.onChange();
}
static defaultProps = {
onChange: (search) => {
}
}
render() {
return (
<input placeholder="Search the tree..." onChange={this.inputChange}/>
);
}
}
export default SearchEngine;
And here is the array and <FileTree>:
let data = [
{
type: "directory",
name: ".",
contents: [
{
type: "directory",
name: "./bin",
contents: [{ type: "file", name: "./bin/greet" }]
},
{
type: "directory",
name: "./lib",
contents: [{ type: "file", name: "./lib/greeting.rb" }]
},
{
type: "directory",
name: "./spec",
contents: [
{ type: "file", name: "./spec/01_greeting_spec.rb" },
{ type: "file", name: "./spec/02_cli_spec.rb" },
{ type: "file", name: "./spec/spec_helper.rb" }
]
},
{ type: "file", name: "./CONTRIBUTING.md" },
{ type: "file", name: "./Gemfile" },
{ type: "file", name: "./Gemfile.lock" },
{ type: "file", name: "./LICENSE.md" },
{ type: "file", name: "./README.md" }
]
}
];
export class FileTree extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNode: null
};
this.setActiveNode = this.setActiveNode.bind(this);
}
setActiveNode(name) {
this.setState({ activeNode: name });
this.props.liftStateUp(name);
}
render() {
return (
<div className="padd_top">
{renderTree(
this.props.root || root,
this.setActiveNode,
this.state.activeNode
)}
</div>
);
}
}
export default FileTree;
I appreciate all the clarity given in this matter and I want to thank in advance all the help you can provide. I'm a ReactJS newbie and in the middle of new understandings.
Thank you.
Your problem is that you have no defined props.onChange function inside SearchEngine class. If your component depends on prop.onChange being a function and you are calling it directly you have to setup required prop
import PropTypes from 'prop-types'
class SearchEngine extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired
}
}
alternatively you can guard your component with
class SearchEngine extends React.Component {
static defaultProps = {
onChange: () => {}
}
}
which will in fact do nothing in case prop is not passed but it will not crash. From your example is not clear what onChange should do but you are not passing it from index.js where you are rendering SearchEngine
EDIT: working solution https://y7p6zlmpyx.codesandbox.io/

Categories