TypeError: Cannot read property 'state' of null
TristanMaurier98 opened this issue Β· comments
π Current behavior
On the sentry of our production app we have this error arriving, it seems to produce crashes but impossible to reproduce locally:
TypeError: Cannot read property 'state' of null
at anonymous(node_modules/instantsearch.js/cjs/widgets/index/index.js:447:26)
at forEach(native)
at dispose(node_modules/instantsearch.js/cjs/widgets/index/index.js:437:27)
at anonymous(node_modules/instantsearch.js/cjs/widgets/index/index.js:219:36)
at reduce(native)
at removeWidgets(node_modules/instantsearch.js/cjs/widgets/index/index.js:217:40)
at anonymous(node_modules/react-instantsearch-core/dist/cjs/lib/useWidget.js:74:36)
at anonymous(node_modules/react-instantsearch-core/dist/cjs/lib/useInstantSearchApi.js:47:19)
at forEach(native)
at anonymous(node_modules/react-instantsearch-core/dist/cjs/lib/useInstantSearchApi.js:46:39)
at apply(native)
at anonymous(node_modules/react-native/Libraries/Core/Timers/JSTimers.js:213:23)
at _callTimer(node_modules/react-native/Libraries/Core/Timers/JSTimers.js:111:15)
at callTimers(node_modules/react-native/Libraries/Core/Timers/JSTimers.js:359:17)
at apply(native)
at __callFunction(node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:427:32)
at anonymous(node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:113:26)
at __guard(node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:368:11)
at callFunctionReturnFlushedQueue(node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js:112:17)
For more context we are running Γ cross platform react-native app, and this error seems to appears at least on ios and android.
π Steps to reproduce
I don't have any, I would be more than glad to share more information.
Live reproduction
/
π Expected behavior
Not having this crashes.
Package version
"instantsearch.js": "^4.57.0", "react-instantsearch": "^7.0.0", "react-instantsearch-core": "^7.1.0",
Operating system
No response
Browser
No response
Code of Conduct
- I agree to follow this project's Code of Conduct
If I read the stack trace correctly, that would be related to
instantsearch/packages/instantsearch.js/src/widgets/index/index.ts
Lines 678 to 692 in 2258d89
helper
would be null, but I imagine the index widget unmounts before init
is ever called? In which case we probably should do less work.
Do you have any more information at all as to when this happens? Are there any unstable references maybe that could cause a remount of an index widget? What widgets do you have mounted, is anything conditional?
Most likely I'd say this is happening on a quick page navigation, or a similar situation in which InstantSearch no longer will be mounted after the error, making the situation less "problematic", but I'm definitely not sure.
From what I can see in my code we only use the react-instantsearch-core lib with the hook: useSearchBox in a rather classic way.
If we look in a little more detail at the stack trace it looks like it goes from react-instantsearch-core to instentsearch.js
I'd say this probably happens in the unmounting phase / navigating. Can you reproduce the error then @TristanMaurier98 ?
I'm having great difficulty reproducing the bug but the project I'm working on is open source, here is the link to the file which I think is the cause of the problem.
https://github.com/pass-culture/pass-culture-app-native/blob/master/src/features/search/components/SearchBox/SearchBox.tsx
Maybe this will help you identify a design flaw.
We've been experiencing this same issue, but only when we're using the InstantSearchNext
component with a nested Index
inside of it. As mentioned above, something happens in that cleanup process that makes state unavailable to that widget when it's disposed.
To get around it for now, I installed patch-package
and uploaded the following file. Not ideal, but keeps the project going while this is looked into further. Hope this helps @TristanMaurier98
diff --git a/node_modules/react-instantsearch-core/dist/es/lib/useInstantSearchApi.js b/node_modules/react-instantsearch-core/dist/es/lib/useInstantSearchApi.js
index 8909dd5..d5cda33 100644
--- a/node_modules/react-instantsearch-core/dist/es/lib/useInstantSearchApi.js
+++ b/node_modules/react-instantsearch-core/dist/es/lib/useInstantSearchApi.js
@@ -144,7 +144,11 @@ export function useInstantSearchApi(props) {
}
return function () {
function cleanup() {
+ try {
search.dispose();
+ } catch(e) {
+ console.warn(`Failed to dispose`, e)
+ }
}
clearTimeout(search._schedule.timer);
// We clean up only when the component that uses this subscription unmounts,
Are you also using React Native @MikeIbberson? Maybe that could be a common thread (although I still don't see the link, sorry!)?
If someone could get a live stack trace, sentry, or when it happens a sort of reproduction, that would be the way to go to find the root cause.
In the very least, I guess what we can do is first check if the index widget's init
has even been called before dispose happens, and skip the dispose of the child widgets, but I'm not sure if that couldn't cause other downstream problems.
One thing I think may be possible in the app, especially linked to
@TristanMaurier98 is that maybe netInfo flickers on/off and the initial state is "enabled", then InstantSearch mounts, but immediately after it's "disabled" and InstantSearch unmounts, before the child widgets properly have mounted.
We're using React 18 (JS not native). We're also using the latest versions of all Algolia packages, though I tried downgrading to releases from last year and observed the same issues.
I will provide more context next week, as there's more development I need to do on this feature and can start building a case. Some things I've noted so far, though, anecdotally:
- I think there's a race condition in the
<Index />
component (or, more specifically, theuseIndex
oruseWidget
hooks). For NextJS, when navigating page-to-page, the<InstantSearchNext />
component seems to be destroying that state before Index has a chance to unmount. Strangely, without that nested Index, no errors. - Inside
<InstantSearchNext />
I'm also seeing some issues with multi-index querying that might be responsible for this initial issue. In our app, we load about 7 different queries, all of which are using a unique Index id (some duplicated index names), but the JSON payload that gets rendered for the SSR provider sheds most of the results. It appears that the server state only reflects the query that takes the longest to execute, so without fail we see hydration errors.
I'll provide proper code snippets as we dig into this more next week, but that's some initial information on what's going on.
Are you also using React Native @MikeIbberson? Maybe that could be a common thread (although I still don't see the link, sorry!)?
If someone could get a live stack trace, sentry, or when it happens a sort of reproduction, that would be the way to go to find the root cause.
In the very least, I guess what we can do is first check if the index widget's
init
has even been called before dispose happens, and skip the dispose of the child widgets, but I'm not sure if that couldn't cause other downstream problems.One thing I think may be possible in the app, especially linked to
@TristanMaurier98 is that maybe netInfo flickers on/off and the initial state is "enabled", then InstantSearch mounts, but immediately after it's "disabled" and InstantSearch unmounts, before the child widgets properly have mounted.
I have try to reproduce by flicking the netinfo but nothing happen (in ios, android and web).
I can sare with you this sentry link https://sentry.passculture.team/share/issue/33f5957c51d04cbab91fe81f7566f26b/
with the precise stack trace.
Current I'm using the Index
inside InstantSearchNextjs
and it happens like @MikeIbberson explained above. When I change to use normal InstantSearch
the error message is gone.
<InstantSearchNext
future={{ preserveSharedStateOnUnmount: true }}
searchClient={searchClient}
routing
>
<Configure hitsPerPage={5} />
<SearchBox />
<Index indexName="leads">
<Hits />
</Index>
<Index indexName="cases">
<Hits />
</Index>
<Index indexName="users">
<Hits />
</Index>
</InstantSearchNext>
The error message:
Uncaught TypeError: helper is null
dispose index.js:449
dispose index.js:439
cleanedState index.js:212
removeWidgets index.js:210
removeWidgets InstantSearch.js:354
[index.js:449](webpack://_N_E/node_modules/.pnpm/instantsearch.js@4.66.1_algoliasearch@4.23.2/node_modules/instantsearch.js/es/widgets/index/index.js?77c0)
dispose index.js:449
forEach self-hosted:203
dispose index.js:439
cleanedState index.js:212
reduce self-hosted:263
removeWidgets index.js:210
removeWidgets InstantSearch.js:354
dispose InstantSearch.js:522
cleanup useInstantSearchApi.js:147
sentryWrapped helpers.js:91
(Async: setTimeout handler)
_wrapTimeFunction trycatch.js:106
store useInstantSearchApi.js:156
safelyCallDestroy react-dom.development.js:20802
commitHookEffectListUnmount react-dom.development.js:20984
...
I have the same problem in my NextJS application when using multi indices and widget :
index.js:513 Uncaught TypeError: Cannot read properties of null (reading 'state')
at eval (index.js:513:27)
at Array.forEach ()
at Object.dispose (index.js:503:20)
at eval (index.js:246:29)
at Array.reduce ()
at Object.removeWidgets (index.js:244:36)
at InstantSearch.removeWidgets (InstantSearch.js:373:22)
at InstantSearch.dispose (InstantSearch.js:541:12)
at cleanup (useInstantSearchApi.js:162:16)
Using @sangdth solution (using InstantSearch component instead of InstantSearchNext component) removes the error.
Hi, I've made a pull request #6173 that should fix this issue, feel free to try it out and give feedback!