react-jsx-highcharts with functional component and formatter - reactjs

I am testing the react-jsx-highcharts possibilities with a polar chart.
Versions:
React: 17.0.1
react-jsx-highcharts: 4.2.0
typescript: 4.0.3
I am using the functional components. So, I don't have any "class" nor "this" in my code.
The chart code is :
<HighchartsProvider Highcharts={Highcharts}>
<HighchartsChart polar plotOptions={plotOptions}>
<Tooltip shared
formatter={tooltipFormatter}
/>
...
</HighchartsChart>
</HighchartsProvider>
All the examples that I have found use this.x, this.value to generate a rich formatter.
For example here or here
I have tried to use the explicit type as:
const tooltipFormatter:TooltipFormatterCallbackFunction = (mycontext: TooltipFormatterContextObject, mytooltip: Tooltip) => {
return "...";
}
but typescript does not accept it, and I cannot find a way to build a clear rich formatter.
Thank you!

I found the solution in their documentation: https://www.npmjs.com/package/react-jsx-highcharts
, where it mentioned, in "Exception 1":
Where Highcharts events are concerned - instead of passing events as an object, we use the React convention onEventName.
So we can use the callback props in <HighchartsChart> element to get the chart object, like so:
const [chart, setChart] = useState({});
const setChart = cha => {
setChart(cha);
}
...
<HighchartsProvider Highcharts={ Highcharts }>
<HighchartsChart plotOptions={ plotOptions } chart={ chartOptions } callback={ setChart }>
...
And we can access the chart object with the chart state.

Related

Property 'width' does not exist on type 'StyleProp<ViewStyle>'

I am working on a React Native app built with TypeScript. I have a component which uses the same props as a View element. If there is a style prop, I need to pull the width in order to pass a calculated width to a child. A simplified example might look like this.
const WidgetContainer = ({ style = {}, ...props }: ViewProps) => {
const { width = DEFAULT_WIDTH } = style;
const widgetWidth = Math.floor(width / 3);
return (
<View style={[style, { width }]} {...props}>
<WidgetRow widgetWidth={widgetWidth} />
</View>
);
};
Everything uses absolute positioning and we can safely assume only numeric widths will be used. This should work. Expo runs the code just fine and nothing seems amiss.
But if I explicitly do a type check, either in VS Code or by running tsc, I get this error on the second line:
Property 'width' does not exist on type 'StyleProp<ViewStyle>'
This is frankly baffling. I use width in View styles all over the code base. If I look up the declaration file for react-native types, I can see ViewStyle:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-native/index.d.ts#L1963
It extends FlexStyle:
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-native/index.d.ts#L692
And width is right there!!
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react-native/index.d.ts#L743
I am at a loss. Is this a bug? Am I missing something silly?
Relevant versions:
react: 17.0.38
react-native: 0.64.3
tsc: 4.3.5
node_modules/expo: 44.0.6
expo CLI: 5.4.9

Change material of gltf-imported mesh programmatically - react three fiber

I'm currently working at a project with react-three-fiber. I've imported the model with useGLTF from #react-three/drei.
With
const { nodes, materials } = useGLTF('/model.glb');
I access the materials from the glb-file.
To access and manipulate the model I used gltfjsx to generate the model.
Now I need to change the material of a mesh programmatically. Because I have no direct access to the JSX of the model I do it with React.cloneElement and modify the props of the mesh.
So I tried something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial ?
<meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> :
materials['mat_7']
});
If overwriteMaterial is false it works. It shows the material it should. But if it's true then the mesh disappears.
I also thought of putting the <meshStandardMaterial /> in the children prop of the mesh. Something like so:
return React.cloneElement(mesh, {
material: overwriteMaterial ? undefined : materials['mat_7'],
children: overwriteMaterial ? <meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> : undefined
});
With this I always get this error and I don't know why it appears:
TypeError: Cannot read properties of undefined (reading 'visible')
Could this approach somehow work or am I doing something completely wrong?
Every help is welcome. Thanks
Alright so I found the answer by myself after some more hours searching for a solution.
The material property doesn't accept a JSX-tag. So if you create an instance of the class MeshStandardMaterial you can pass it to the property and it works perfectly fine. Now it looks something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial
? new MeshStandardMaterial({ color: 0x0ff000 })
: materials['mat_7']
})
Note: The class MeshStandardMaterial is exported from the three package.
i really don't think you have to clone any react element, that doesn't seem correct. you can clone or mutate materials just as you would in a plain three app. i have no idea why you even want to clone jsx.
const { scene } = useGLTF(url)
const clonedScene = useMemo(() => scene.clone(), [])
useLayoutEffect(() => {
clonedScene.traverse(o => {
if (o.type === 'Mesh') {
o.material = ...
}
})
}, [clonedScene]}
return <primitive object={clonedScene} />
you can skip the clonedScene thing completely as well, this is only if you plan to re-use the model in your scene multiple times.

Importing RangeValue type from Ant Design

Working on an update to Replace Moment.js with date-fns for Ant Design's DatePicker based on the documentation which seems to be working fine.
Mainly it's suggesting to create the following:
import dateFnsGenerateConfig from 'rc-picker/lib/generate/dateFns'
import generatePicker from 'antd/es/date-picker/generatePicker'
const DatePicker = generatePicker<Date>(dateFnsGenerateConfig)
Then use the component as below:
<DatePicker.RangePicker
placeholder={['From', 'To']}
onChange={range => setRange(range)}
value={range}
/>
For the above the following state has been created with the type RangeValue:
const [range, setRange] = useState<RangeValue<Date>>(
[from, to]
)
The RangeValue type has been imported as the following:
import { RangeValue } from 'rc-picker/lib/interface'
// technically:
// import { RangeValue } from '../../../../node_modules/rc-picker/lib/interface'
Question:
I found only this comment in one of the questions here where we have similar conversation.
Is there any way to import the RangeValue type, maybe from Ant Design? Thank you!
I was having a hard time typing antd values. Seems like they don't provide any easy option. Eventually ended up on using this:
type RangeValue = Parameters<NonNullable<React.ComponentProps<typeof DatePicker.RangePicker>['onChange']>>[0]
That will pull out the type of the first argument that is sent into the onChange function. It will also work with other events (onClick, onBlur etc). You can also pull out the types of other arguments simply by increasing the number in the square brackets.
Now you can assign this type to your onChange handler:
const handleOnChange = (dates: RangeValue, formattedDates: string[]) => {
//do something
return dates
}

Referencing a Highcharts object as a React element (typescript)

I need to set some conditional properties of a Highcharts object and then render it. This is my trial and error code and results so far.
const Charts = ReactHighcharts.withHighcharts(Highstock);
public render(): React.ReactNode {
const chart = new Charts();
// set some passed in options
const chartOptions: Options | undefined = this.props.opts;
chart.config = chartOptions;
if(this.drillable()) {
chartOptions.chart = {};
chartOptions.chart.events = {
drilldown: (event: Highcharts.ChartDrilldownEvent) => {
if (!event.seriesOptions) {
if(event.point !== undefined) {
// #ts-ignore
const pathRoot: List<number> = event.point.path;
const drilldownSeries = this.getDrillSeries(pathRoot);
chart.addSeriesAsDrilldown(event.point, drilldownSeries);
}
}
}
};
}
// maybe add some other conditional configurations
return (
<div>
{ chart }
</div>
);
}
This results in "Objects are not valid as a React child. If you meant to render a collection of children, use an array instead"
Wrapping it in an array like this and trying to render the 'elements' gives the same error
const elements: any[] = [];
elements.push(chart);
Also tried some variations of creating JSX elements like this
const elements: JSX.Element[] = [];
elements.push(React.createElement(chart));
which results in: "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. "
Also tried just rendering just the element, as in
{ React.createElement(chart) }
which gives the same error again.
Edit:
I think I'm getting there but still missing something. i.e.
<ReactHighcharts
config={chartOptions}
callback={this.chartCallback}
/>
I set up my 'chartOptions', and then in chartCallback, I need to mutate those same chartOptions again to add the events(chartOptions.chart.events = {drilldown: etc}).
I tried pulling the chartOptions from the chart object inside the callback but this does not mutate the config. Is there a way to get access to the config inside the callback ? Thanks again.
I suggest you use the official wrapper. https://github.com/highcharts/highcharts-react
This has a callback function. The first argument is the chart.
Beside of this you should get the chart config ready before you actually render it.
Finally got somewhere - you do not actually need the callback to access the chart object, nor do you need to 'capture' a reference to it like I had been trying in my original post. It is available in the target property of the event.
e.g.
drilldown: (event: Highcharts.ChartDrilldownEvent) => {
const highChart = event.target as Highcharts.ChartObject;
// ...
highChart.addSeriesAsDrilldown(event.point, someNewSeries);
}

ReactJS Slider carousel component

Following some code examples, I've found this:
<Slider ref = {c => (this.slider = c)} {...this.settings}>
{
//custom component for slider content
}
</Slider>
I don't get what's the meaning of ref = {c => (this.slider = c)} {...this.settings}. What is this doing? this.settings is an object with various properties, like arrows:false, mobilefirst:true. But I don't know this construct of ref etc. and in the example is not explained.
Is there a guide for this?
This code creates reference to the element to work with it later - it is stored on the class and can be accessed with this.slider or passed down as a prop to children. For example, it can be used to set focus just like with regular HTML element: this.slider.focus(). You can read more about callback refs here: https://reactjs.org/docs/refs-and-the-dom.html#callback-refs

Resources