Vue3 Functional Component render function Primevue Tooltip - javascript

rowTooltip: (createElement, cell, property, context) => {
return withDirectives(createElement("div", "any value"), ["tooltip", "is this the tooltip value?", "is this the value actually?", "no this is the value argument"]);
},
how do I render this tooltip directive : https://www.primefaces.org/primevue/tooltip
inside a render function loop inside a functional component with vue3.
I've been trying for hours
added a fork with my problem

vue3 has function h
using with setup function:
setup() {
const tooltip = resolveDirective('tooltip');
return () => {
const vnode = withDirectives(
h(Button, { label: "I'am dynamic button, hover me too" }),
[[tooltip, 'tooltip directive with render function']]
);
return vnode;
};
}
example: https://stackblitz.com/edit/vue-gg76si?file=src/main.js

Related

Anychart Lib is duplicating 1 component on every render

this is my first post, im using Anychart library with React in a component build, i was able to implementing but the way that anychart recomend to use in every render the chart is duplicated.
This is my component
const Chart: React.FunctionComponent = () => {
function charts() {
// create data
const data = [
{
x: '2022-07-26',
y: '0.29822798232939185',
},
];
// create a chart and set the data
const chart = anychart.line();
chart.data(data);
// set the chart title
chart.title('Sales of the Most Popular Products of ACME Corp.');
// set the titles of the axes
chart.xAxis().title('Year');
chart.yAxis().title('Revenue');
// draw
chart.container('container');
chart.draw();
}
React.useEffect(() => {
charts();
}, []);
return <StyledDiv id="container" />;
};
export default Chart;
As you see, is very simple, but every time the app makes a render this component is duplicated and generate a new chart.
This problem can be solved by returning the chart itself in the charts() function.
function charts() {
//your code here
// draw
chart.container('container');
chart.draw();
//returning chart variable
return chart
}
Then chart.dispose() function must then be returned in a useEffect hook.
useEffect(() => {
const chart = charts();
// Return a cleanup function to remove the chart when the component unmounts
return () => chart.dispose();
}, []);
This problem appears due to React LifeCycle, and it can be solved by deleting duplicates when it has been rendered, which is what the dispose function does. You can use the example of the dispose function, which prevents duplicates in React.

Vue Watcher not working on component created with Vue.extend

I have a Parent component with a select input which is bound through v-model to a variable in data.
Besides, I create child components dynamically using Vue.extend, which i pass the propsData which also includes the value of the select.
This components have a watcher for the prop that is related to the select input.
When i create the component it receives the props succesfully, The problem comes when I update the value of the select input that doesn't trigger the watcher on the child component.
I've been looking for similar situations but have not found something that helps me solve this problem, i don't know why it doesn't trigger the watcher on the child component when the select input changes.
Any help would be very preciated.
Here i create the component dynamically:
let PresupuestoFormularioVue = Vue.extend(PresupuestoFormulario)
let instance = new PresupuestoFormularioVue({
propsData: {
//The prop related to select input
seguro: this.seguro,
}
})
instance.$mount()
this.$refs.formularioContenedor.appendChild(instance.$el)
And this is the watcher in the component which isn't working:
watch:{
seguro:{
handler: function( newVal ){
console.log(newVal)
},
},
},
It's not the watch that doesn't work. It's the bindings. You're assigning the current value of this.seguro, not the reactive object itself. However, a new Vue() can add this binding for you.
As a sidenote, whether PresupuestoFormulario is a Vue.extend() doesn't matter. It can be any valid VueConstructor: a Vue.extend(), Vue.component() or a valid SFC (with name and template): export default {...}.
Here's how to do it:
methods: {
addPresupuestoFormulario() {
const div = document.createElement('div');
this.$el.appendChild(div);
new Vue({
components: { PresupuestoFormulario },
render: h => h("presupuesto-formulario", {
props: {
seguro: this.seguro
}
})
}).$mount(div)
}
}
The <div> initially appended to the parent will get replaced upon mounting with the actual template of PresupuestoFormulario and the bindings will be set, exactly as if you had <presupuesto-formulario :seguro="seguro" /> in the parent template from the start.
The really cool part about it is that the parent component doesn't need to have PresupuestoFormulario declared in its components.
Here's a working example:
const Test = Vue.component('test', {
template: `<div>message: {{message}}</div>`,
props: ['message'],
watch: {
message: console.log
}
})
new Vue({
el: '#app',
data: () => ({
msg: "¯\\_(ツ)_/¯"
}),
methods: {
addComponent() {
const div = document.createElement("div");
this.$el.appendChild(div);
new Vue({
components: {
Test
},
render: h => h("test", {
props: {
message: this.msg
}
})
}).$mount(div);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2"></script>
<div id="app">
<input v-model="msg">
<button #click="addComponent">Add dynamic child</button>
</div>
A separate note, about using this.$el.appendChild(). While this works when you're using a root Vue instance (a so-called Vue app), it will likely fail when using a normal Vue component, as Vue2 components are limited to having only 1 root element.
It's probably a good idea to have an empty container (e.g: <div ref="container" />) in the parent, and use this.$refs.container.appendChild() instead.
All of props that you want check in watcher, should be a function. If you want read more about this go to vue document codegrepper.
watch: {
// whenever seguro changes, this function will run
seguro: function (newValue, oldValue) {
console.log(newValue,oldValue)
}
}

AG-Grid React, trouble getting custom cell renderer to update when data changes. Function components are behaving differently than class components

I'm using AG-Grid throughout a react application, and in several instances I want a cellRenderer to update when grid data changes. In both of the following cases, the cellRenderer loads correctly initially, but doesn't update when data changes:
A user edits an editable cell and changes the value.
The server updates the grid data in redux
Check out this codesandbox
In this example I recreated the first use case with an editable cell, and used a class component and a function component. Using onCellValueChanged with params.api.refreshCells() I was able to get the class component to update, but not the function component.
First question: How do I get react function components to re-render with new props, the same way the class component re-renders in the codesandbox example?
Second question: Is there a better way to update a cellRenderer without un-mounting and re-mounting every cell in the column any time data updates?
Thanks in advance for the help and guidance!
...
this.state = {
columnDefs: [
{
headerName: "Editable Country",
editable: true,
field: "country",
width: 200
},
{
headerName: "Class Render",
cellRendererFramework: GridCellClass,
colId: "class-renderer",
width: 200
},
{
headerName: "Function Render",
cellRendererFramework: GridCellFunction,
colId: "function-renderer",
width: 200
}
],
rowData: []
};
}
...
<AgGridReact
rowModelType="infinite"
columnDefs={this.state.columnDefs}
enableColResize={true}
rowClassRules={{
california: function(params) {
return params.data.country === "California";
}
}}
onCellValueChanged={function(params) {
return params.api.refreshCells({
force: true,
columns: ["class-renderer", "function-renderer"]
});
}}
onGridReady={this.onGridReady.bind(this)}
rowData={this.state.rowData}
/>
...
// This one updates!
import React, { Component } from "react";
class GridCellClass extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { data } = this.props;
return (
<div>
{data.country === "California" ? "CALIFORNIA!!!" : "Not California"}
</div>
);
}
}
export default GridCellClass;
// This one does not update.
import React from "react";
function GridCellFunction(props) {
const { data } = props;
return (
<div>
{data.country === "California" ? "CALIFORNIA!!!" : "Not California"}
</div>
);
}
export default GridCellFunction;
1.Cell Renderer Function
A cell renderer function should be used when you dont have refresh requirements and this is mentioned in the ag-grid docs.
As per docs-
Use the function variant of a cell renderer if you have no refresh or
cleanup requirements (ie you don't need to implement the refresh or
destroy functions).
This is the reason why your cell renderer function does not refresh.
To solve your problem, you can do this -
onCellValueChanged={function(params) {
if(params.column.getId() === 'country') {
params.api.redrawRows();
}
}}
2.Cell Renderer Component
The reason your GridCellClass works on api.refreshCells() is because the grid handles the refresh() for you since you have not implemented refresh() in your GridCellClass component.
What that means is your component will be destroyed and recreated if the underlying data changes.
3.RefreshCells
Also note that using api.refreshCells() won't work as is because ag-grid uses change detection to refresh cells while determining what cells to refresh and since essentially the value of your other 2 columns do not change but infact they are changed in the cellRenderer itself.
However the below works for GridCellClass because you disable change detection by passing force:true,
params.api.refreshCells({
force: true
});
From docs-
Change detection will be used to refresh only cells who's display cell
values are out of sync with the actual value. If using a cellRenderer
with a refresh method, the refresh method will get called.

Exposing the state of a react widget

I have made a react UI widget thats let's the user select a number of different times and dates. The user's current selection is stored in the state of a top level component, DateTimePicker. I then have a widget wrapper like so:
import ...
export default {
new: (args) => {
const store = {
reactElement: <DateTimePicker
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return store.reactElement.getState(); // DOESN'T WORK
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
I want to add a validation to make sure that at least X days/times are selected, but this validation needs to be implemented outside of the widget.
For this, I'll need someway of asking the widget of it 's state. i.e. what has the user selected? Although it seems like the state of the class is not part of the public api of a react component.
How can I acess the state, or is there another way I'm missing?
The solution to doing things imperatively from the parent to the child usually involves getting a ref to the child component. Something along these lines:
export default {
new: (args) => {
let myRef = React.createRef();
const store = {
reactElement: <DateTimePicker
ref={myRef}
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return myRef.current.getState();
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
With ref={myRef} added as a prop, whenever DateTimePicker gets mounted, it will assign a reference to the mounted component to myRef.current. You can then use that reference to interact directly with the most recently mounted component.

If render functions should be pure, how the view can get changed based on state?

I am new to react and while going through the tutorials I found this ,
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
I am little confused with this. If render function should return same result each time how can I modify the display based on states ?
For example I have text with edit button. When I click on edit, text-box should appear with content of text and edit button changes to save.
"The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not directly interact with the browser." - https://facebook.github.io/react/docs/react-component.html#reference
is absolutely a correct statement in react. Changing a state cannot be done can't inside a render method. You can access the state inside the render but change. To change the state we use setState function in callback method which set the new state and ready to change anywhere in your component.
Note: setState expects the state to be initialized first. getInitialState is the function for the same and given in example below
eg.
var firstName = 'Anand';
var testExample = React.createClass({
getDefaultProps: function () {
return {
name: 'React',
message: 'This is the default message!'
};
},
getInitialState: function () {
return {
name: this.props.name
};
},
handleNewName: function (name) {
this.setState({
name: name
});
},
render: function () {
var name = this.state.name;
var message = this.props.message;
return (
<div>
<h1>{message}</h1>
<h2>{name}</h2>
</div>
);
}
});
Inside the handleNewName function we have changed the state which then used inside render. Hope this helps

Categories

Resources