Call a parent component function from a child component - javascript

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>;
}
}

Related

Error 'Do not use findDOMNode'; how to fix the code so it doesn't complain?

I have a javascript class that is in my react project.
Basically, i wanted to add a sunburst pie chart to my UI. So I put the code for it in my react project. At compile time (i.e. when i run npm start) I get the error
error Do not use findDOMNode react/no-find-dom-node
I read online but I still don't completely understand this error (or what findDOMNode does, for that matter).
I simply need to fix the code for findDOMNode to whatever it needs to be, as for now I am just disabling the rule.
import React from "react";
import ReactDOM from "react-dom";
import Sunburst from "sunburst-chart";
/* eslint-disable react/no-find-dom-node */
/* eslint-disable no-console */
const data = {
name: "main",
color: "magenta",
children: [
{
name: "a",
color: "yellow",
size: 1
},
{
name: "b",
color: "red",
children: [
{
name: "ba",
color: "orange",
size: 1
},
{
name: "bb",
color: "blue",
children: [
{
name: "bba",
color: "green",
size: 1
},
{
name: "bbb",
color: "pink",
size: 1
}
]
}
]
}
]
};
class SunburstChart extends React.Component {
constructor() {
super();
this.state = {
myChart: Sunburst().data(data)
};
}
componentDidMount() {
// set el height and width etc.
this.state.myChart(ReactDOM.findDOMNode(this));
}
onSelect(event) {
console.log(event);
}
render() {
return <div id="chart" />;
}
}
export default SunburstChart;
Facebook will eventually deprecate findDOMNode as it blocks certain improvements in React in the future.
It is recommended to use callback refs instead. See Dan Abramov comments and examples here:
https://github.com/jsx-eslint/eslint-plugin-react/issues/678#issue-165177220
source: https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-find-dom-node.md
I was able to work it out after reviewing the github links
componentDidMount() {
// set el height and width etc.
this.state.myChart(this.node);
}
onSelect(event) {
console.log(event);
}
render() {
return <div id="chart" ref={node => this.node = node}/>;
}

How to access vanilla class instance stored from one Vue component in a parent component?

I have a parent component with 2 child components that both inherit from the same base component. (This parent component is being created and used in a Vue Storybook). Both SiblingAComponent and SiblingBComponent inherit the same BaseComponent, and instantiate the same inherited data classInstance, which is a vanilla JS class instance from another library. I am trying to access this classInstance from the parent component to pass as data into the second sibling component (in this case, from SiblingAComponent to SiblingBComponent), by using an reference siblingARef. However, I get this error from the storybook compiler:
too much recursion
isArguments#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:49010:16
keys#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:49073:28
_traverse#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:119972:19
_traverse#http://localhost:6006/vendors~main.9107ef8d0bc0558399e1.bundle.js:119974:28
ParentComponent Story:
storiesOf("ParentComponent Story", module)
.addDecorator(
withKnobs({
escapeHTML: false
})
)
.add("Passing data from A to B", () => ({
name: 'ParentComponent',
components: {
SiblingAComponent,
SiblingBComponent,
},
data() {
return {
siblingAData: [....], // array of objects
siblingAOptions: {
axes: {},
height: "50px",
},
siblingBData: [...], // array of objects
siblingBOptions: null,
}
},
mounted() {
const siblingAInstance = this.$refs.siblingARef.classInstance;
const newOptions = {
legend: {
external: {
reference: siblingAInstance,
},
},
};
// this line is where I am getting an error
this.siblingBOptions = legendExternal;
},
template: `
<SiblingAComponent ref="siblingARef" :data="siblingAData" :options="siblingAOptions"/>
<SiblingBComponent v-if="siblingBData" :data="siblingBData" :options="siblingBOptions"/>
`,
}));
SiblingAComponent:
<template>
<div class="sibling-a-component"></div>
</template>
<script>
import { ComponentA } from '#libraryexample/components';
import BaseComponent from './base-component.vue';
export default {
name: 'SiblingAComponent',
extends: BaseComponent,
mounted() {
this.classInstance = new ComponentA(this.$el, {
data: this.data,
options: this.options,
});
},
};
</script>
SiblingBComponent:
<template>
<div class="sibling-b-component"></div>
</template>
<script>
import { ComponentB } from '#libraryexample/components';
import BaseComponent from './base-component.vue';
export default {
name: 'SiblingBComponent',
extends: BaseComponent,
mounted() {
this.classInstance = new ComponentB(this.$el, {
data: this.data,
options: this.options,
});
},
};
</script>
BaseComponent:
<script>
export default {
name: 'BaseComponent',
data() {
return {
classInstance: null,
};
},
props: {
data: { type: [Object, Array], required: true },
options: { type: Object, required: true },
},
};
</script>
Coming from the Angular and React worlds, using a reference to access a Vanilla class instance from another Component is nothing new, even if it's unconventional. I am new to Vue, so I am wondering why would trying to access a class instance fail (works fine for primitive data types) and give me such a weird error? Where is the recursion occurring?

How to access properties of objects from a third-party DOM library used in a React component from the outside

I want to use Vis.js in a React based project. Since none of the Vis Network implementations work for me I have to use the plain library.
This is my test React component
import { DataSet, Network } from 'vis';
import React, { Component, createRef } from "react";
class VisNetwork extends Component {
constructor() {
super();
this.network = {};
this.appRef = createRef();
this.nodes = new DataSet([
{ id: 1, label: 'Node 1' },
{ id: 2, label: 'Node 2' },
{ id: 3, label: 'Node 3' },
{ id: 4, label: 'Node 4' },
{ id: 5, label: 'Node 5' }
]);
this.edges = new DataSet([
{ from: 1, to: 3 },
{ from: 1, to: 2 },
{ from: 2, to: 4 },
{ from: 2, to: 5 }
]);
this.data = {
nodes: this.nodes,
edges: this.edges
};
this.options ={};
}
componentDidMount() {
this.network = new Network(this.appRef.current, this.data, this.options);
}
render() {
return (
<div ref={this.appRef} />
);
}
}
export default VisNetwork;
It's the only component mounted so far
ReactDOM.render(<VisNetwork />,document.getElementById('mynetwork'));
My question is how I can access the properties of the network, for example, to get or delete a node.
node = nodes.get(nodeId);
I read about React Ref and tried something like
() =>{ console.log(document.getElementsByClassName('vis-network')) as callback of ReactDOM.render()but that doesn't help.
Another question is why isn't the ref not set and it's just <div>.
Because I thought that this.appRef = createRef(); in the constructor of the component and ref={this.appRef} in render() would lead to a ref.
I hope you can give me a hint.
Actually the flow should be the other way round, define the nodes outside of the component, then pass them in. For that define the constructor as:
constructor(props) {
super(props);
this.nodes = props.nodes;
//...
}
Then construct it as:
const nodes = new DataSet([/*...*/]);
ReactDOM.render(<VisNetwork nodes={nodes} />,document.getElementById('mynetwork'));
Note that you should put anything stateful into this.state and use setState to mutate it.

React Native Highcharts call method

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>
);
}
}

Vuejs Component Route Dynamic Selection

I've got a Vue instance:
new Vue({
el: '#Application',
router: Router,
components: { 'ExampleDepartment', Application },
data: function() {
return {
}
}
});
Inside of my application file, I import the template, sidebar action. Inside the template, I have the following:
<v-list-tile v-for="action in actions" :key="action.label" v-if="action.visibility == true">
...
</v-list-tile>
Then inside, I have the following:
export default {
watch: {
$route: function() {
this.getOrSetPageVisibility();
}
},
methods: {
getOrSetPageVisibility() {
for(let index = 0; index < this.actions.length; index++) {
if(this.actions[index].page == this.$router.currentRoute.name) {
this.actions.$set(index, { visibility }, true);
}
}
}
},
data: function() {
return {
actions: [
{
label: 'Add Sample',
icon: 'add_circle',
page: 'Sample',
visibility: false
}
]
}
}
}
So the issue, is every time the page switches I want to load a variation of menu items to the sidebar, but it won't let me modify the array. It complains that $set isn't valid or undefined, and it won't update the component on the change.
I can see that on the menu changes it executes my method through the watch, but fails when modifying the array.
How can I dynamically add to the menu based on selected page?
You are using the Vue.set method incorrectly.
It is available on a Vue instance via this.$set. It should look like this:
this.$set(this.actions[index], 'visibility', true);

Categories