Error from Flexmonster - React

Closed
Sophie L asked on February 3, 2022

Hello again, I decided to post another problem I found.
I implemented the logic discussed here in our real application which uses React.
Upon changing the reference data in a parent component, the data is updated in Flexmonster.
But whenever the data is changed, we're getting the following error from Flexmonster in the developer console: 

Uncaught TypeError: Cannot read properties of null (reading 'element')
at new k (flexmonster.full.js:99:1)
at h.Fe (flexmonster.full.js:1572:1)
at flexmonster.full.js:1561:1
k @ flexmonster.full.js:99
Fe @ flexmonster.full.js:1572
(anonyme) @ flexmonster.full.js:1561
requestAnimationFrame (asynchrone)
requestAnimationFrame @ flexmonster.full.js:149
u.$n.load.ON @ flexmonster.full.js:1559
load @ flexmonster.full.js:86
h @ flexmonster.full.js:1559
k @ flexmonster.full.js:2365
PivotApi @ flexmonster.full.js:2383
(anonyme) @ flexmonster.full.js:2393
(anonyme) @ index.js:1
invokePassiveEffectCreate @ react-dom.development.js:23487
callCallback @ react-dom.development.js:3945
invokeGuardedCallbackDev @ react-dom.development.js:3994
invokeGuardedCallback @ react-dom.development.js:4056
flushPassiveEffectsImpl @ react-dom.development.js:23574
unstable_runWithPriority @ scheduler.development.js:468
runWithPriority$1 @ react-dom.development.js:11276
flushPassiveEffects @ react-dom.development.js:23447
(anonyme) @ react-dom.development.js:23324
workLoop @ scheduler.development.js:417
flushWork @ scheduler.development.js:390
performWorkUntilDeadline @ scheduler.development.js:157

The implementation of the component looks roughly as below (the actual code is more complex).
The code is actually more complex, and may contain errors, but my first question here would be more to know how to debug the problem on my side. And to find out if this 

import React, { useEffect, useRef, useState } from 'react';
import { FlexmonsterReference, Pivot } from 'react-flexmonster/hooks';

// Illustration - Actual code may differ
const MyComponent: React.FC<any> = ({
licenseKey,
data,
config,
localization,
}) => {
const flexmonsterRef = useRef<FlexmonsterReference>(null);
const [report, setReport] = useState<any>([]);

useEffect(() => {
const x = {
...config,
localization,
dataSource: { data },
}
setReport(x);
}, [data, config, localization]);

// none of these are triggered
const handleDataError = (params: any) => console.log('dataerror', params);
const handleLocalizationError = () => console.log('localizationerror');
const handleQueryError = () => console.log('queryerror');

return (
<Pivot
ref={flexmonsterRef}
licenseKey={licenseKey}
report={report}
dataerror={handleDataError}
localizationerror={handleLocalizationError}
queryerror={handleQueryError}
/>
);
};

 

Tanya Gryshko created this ticket from #44097

6 answers

Public
Nadia Khodakivska Nadia Khodakivska Flexmonster February 4, 2022

Hello, 
 
Thank you for reaching out to us.
 
We haven't managed to reproduce the described issue on our side. Could you please provide us with the sample project where the reported issue can be reproduced? It will help us to continue the investigation.
 
Looking forward to hearing from you.
 
Kind regards,
Nadia

Public
Nadia Khodakivska Nadia Khodakivska Flexmonster February 7, 2022

Hello, Sophie,
 
As a follow-up to our previous answer, please check the suggestion below.
 
In your code sample, there's importing from react-flexmonster/hooks:

import { FlexmonsterReference, Pivot } from 'react-flexmonster/hooks';

We suggest importing instead:

import * as Pivot from 'react-flexmonster';

 
Please let us know if such an approach would work for your case. Looking forward to your response.
 
Kind regards,
Nadia

Public
Sophie L February 7, 2022

Hello Nadia,
This didn't resolve the problem.
I looked at our code, and I think the reason why the initial developer (now gone) imported from `react-flexmonster/hooks` is likely that he wanted to import FlexmonsterRefefence to be used in `const flexmonsterRef = useRef<FlexmonsterReference>(null);`
After changing to main FlexmonsterReact.Pivot, FlexmonsterReference` doesn't work anymore to type the ref (I used any for now).
If you think that not importing from hooks is preferable, we'll do it anyway.

Public
Sophie L February 7, 2022

Hello again.
I may have found one of the sources of the problem, but that doesn't solve everything, and I'll explain, because I'd like to get to the bottom of it and make sure that we do things properly.
So, back to my example, the problem I found is that a key prop was added to the Pivot to explicitly force React to recreate the component.

import * as FlexmonsterReact from 'react-flexmonster';
const MyComponent: React.FC<any> = (data, config) => {
  const flexmonsterRef = useRef<any>();  // was: useRef<FlexmonsterReference>
  return <FlexmonsterReact.Pivot
    key={random()} // Important: Do not remove
report={getReport(computeReport(data, config)}
      ref={flexmonsterRef}
    ... />
 }

 
Removing the key` seems to solve the problem, but it came with a warning and a fat comment from the initial developer (now gone):

Flexmonster is providing a React wrapper for their product. Under the hood, they use a global instance of their library. I noticed they create and dispose their instance automatically when you render the component.
There is a problem when you re-render the same component too quickly. They might kill their global instance between 2 renders and you'll end up not having access to their instance (remember all random error we had with wdr "this.q8 is undefined"...)

The solution that was implemented was to:

  1. Memoize incoming props (the functional equivalent of shouldComponentUpdate` React Hooks - How do I implement shouldComponentUpdate?
  2. Add a debounce before our component to avoid triggering props changes too often in Flexmonster Pivot.
  3. Force reinstantiating the React component at each render => `<FlexmonsterReact.Pivot key={random} />`

If memoizing may sometimes be necessary (point 1, equivalent to shouldComponentUpdate`), debouncing starts looking like a hack, and forcing a random key definitely calls for scrutiny, as forcing a React component to be rerendered is costly, but also doesn't guarantee that the underlying non react flexmonster instance would indeed be removed / recreated as we'd like.

  • Is there indeed a single "global" instance of Flexmonster behind Pivot (looking at the exposed code, I agree that it's  not clear to me)?
    • To give you more context, our application implements several pages exposing different reports/configurations and displaying various charts (calling getData()`I think). If behind the scene a single instance of Flexmonster is used, could it be that some config spilled from that global instance into our pages and somehow "corrupt" some of our reports by reporting data from a previous report?
    • Also we were initially using WebDataRocks when we started.
    • Could we have 2 distinct <Pivot> components coexisting in the same page?
  • Is there indeed a risk if updating props too quickly?

So, again, I apologize if I didn't expose all our internal "cuisine", but as I mentioned above, I'd rather get to the bottom of it to ensure that our application runs smoothly without risking data corruption.

Public
Sophie L February 7, 2022

Another quick update to mention that I'm continuing the investigation on my side, as some of my previous conclusions may have been a bit too hasty. Right now, the debouncing I mentioned seems like it might be the culprit, but I will come back to you.

Public
Nadia Khodakivska Nadia Khodakivska Flexmonster February 9, 2022

Hello,
 
Thank you for the detailed information.
 
Please check the answers below:
 

  • Speaking of your assumption about a single “global” instance of Flexmonster behind Pivot:
     - Flexmonster is a JavaScript library. Since JavaScript is an object-based language based on prototypes - each Flexmonster instance is independent and does not affect other ones. Here you can see how new Pivot instance is created: https://github.com/flexmonster/react-flexmonster/blob/218a230f52bcba99af2bb7e8dd4bd6879160190f/src/index.js#L15

     - That is why 2 distinct <Pivot> components can coexist in the same application without any harm

  • Regarding the updating props too quickly, it's not entirely clear for us if it could be the problem. However, if it makes the component recreating and re-rendering each time, that can cause a harmful impact on performance. We recommend creating Flexmonster component once and then updating it using API calls such as updateDatasetReportrunQuery, etc. If the recreation cannot be avoided - make sure that you handle the process using Flexmonster events.

 
We hope it helps. You are welcome to write to us in case further questions arise.
 
Kind regards,
Nadia

This question is now closed