<p>react-dnd peut appeler un composant non monté provoquant une « Erreur non détectée : impossible de trouver un targetId valide »</p>

Créé le 2 juin 2019  ·  3Commentaires  ·  Source: react-dnd/react-dnd

Décrivez le bogue
il est possible pour react-dnd d'appeler une fonction de collecte qui provoque le démontage d'un composant, puis d'effectuer un autre appel collect() sur le composant non monté, provoquant une erreur non détectée "Impossible de trouver un targetId valide".

Redux sur son envoi () des événements backend Drag And Drop fait une copie de tous les auditeurs aux événements abonnés au début et les appelle toujours quels que soient les abonnements redux qui ont été désabonnés tout en notifiant les abonnés (voir https://stackoverflow.com/questions /43356080/redux-unsubscribe-within-componentwillunmount-still-calls-subscribe-callback). Cela signifie que si quelque chose comme un événement de glisser html5 'EndDrag' déclenche une notification dans handleChange() dans react-dnd/src/decorateHandler.tsx:handleChange(), et que le premier appel collect() provoque le démontage d'un composant, le le composant non monté suivant peut avoir son appel collect() déclenché dans une notification ultérieure du même événement.

Ainsi, l'invariant « Attendu trouver une cible valide » est déclenché car le composant a été validement supprimé du registre des identifiants de cible valides, mais la notification appelle un composant non monté.

J'ai découvert ce problème en essayant d'intégrer react-sortable-tree, qui utilise react-dnd dans mon application. Vous pouvez voir un exemple du problème et un cas de test recréable ici : https://github.com/frontend-collective/react-sortable-tree/issues/490. Un glisser-déposer qui est annulé en libérant un dragSource en dehors du dragTarget reproduit ce problème à chaque fois.

L'erreur non détectée et la trace de la pile sont collées ci-dessous. La trace de la pile montre que la fonction de connexion appelle monitor.canDrop() qui échoue sur les composants non montés.
browser.js:38 Violation invariante non capturée : devrait trouver une cible valide.à invariant (https://xzoq6xprlz.codesandbox.io/node_modules/invariant/browser.js:38:15)à DragDropMonitorImpl.canDropOnTarget (https://xzoq6xprlz.codesandbox.io/node_modules/dnd-core/lib/cjs/DragDropMonitorImpl.js:67:9)
à DropTargetMonitorImpl.canDrop (https://xzoq6xprlz.codesandbox.io/node_modules/react-dnd/lib/cjs/DropTargetMonitorImpl.js:24:41)
à nodeDropTargetPropInjection (https://xzoq6xprlz.codesandbox.io/node_modules/react-sortable-tree/dist/index.cjs.js:2367:28)
à DragDropContainer.getCurrentState (https://xzoq6xprlz.codesandbox.io/node_modules/react-dnd/lib/cjs/decorateHandler.js:116:29)
à DragDropContainer._this.handleChange (https://xzoq6xprlz.codesandbox.io/node_modules/react-dnd/lib/cjs/decorateHandler.js:45:39)
à handleChange (https://xzoq6xprlz.codesandbox.io/node_modules/dnd-core/lib/cjs/DragDropMonitorImpl.js:27:21)
à l'expédition (https://xzoq6xprlz.codesandbox.io/node_modules/redux/lib/redux.js:220:7)
à Object.eval [comme endDrag] (https://xzoq6xprlz.codesandbox.io/node_modules/dnd-core/lib/cjs/DragDropManagerImpl.js:67:21)
sur HTML5Backend.handleTopDragEndCapture (https://xzoq6xprlz.codesandbox.io/node_modules/react-dnd-html5-backend/lib/cjs/HTML5Backend.js:145:31)
``

solution de contournement

Appeler des composants non montés n'est probablement pas le bon comportement, j'ai donc ajouté une instruction if dans react-dnd/src/decorateHandler.tsx:handleChange qui supprime la notification si le decorHandler a déjà été désabonné. diff attaché.

diff --git a/packages/react-dnd/src/decorateHandler.tsx b/packages/react-dnd/src/decorateHandler.tsx
index 85385ec..bcd149e 100644
--- a/packages/react-dnd/src/decorateHandler.tsx
+++ b/packages/react-dnd/src/decorateHandler.tsx
@@ -159,6 +159,15 @@ export default function decorateHandler<Props, CollectedProps, ItemIdType>({
                }

                public handleChange = () => {
+                       if (this.disposable.isDisposed) {
+                               console.log("in handleChange")
+                               //because redux takes a snapshot of all subscribers to 
+                               //events when it starts dispatch, it is still possible to call into this even after 
+                               //the subscription has been unsubscribed
+                               //to prevent against calling into unmounted objects, return immediately
+
+                               return
+                       }
                        const nextState = this.getCurrentState()
                        if (!shallowEqual(nextState, this.state)) {
                                this.setState(nextState)
wontfix

Commentaire le plus utile

@ mx2323 a-

Tous les 3 commentaires

Ce problème a été automatiquement marqué comme obsolète car il n'a pas eu d'activité récente. Il sera fermé si aucune autre activité ne se produit. Merci pour vos contributions.

@ mx2323 a-

je n'ai pas fini par le pousser.

Cette page vous a été utile?
0 / 5 - 0 notes