MODIFIER: une meilleure explication

Le contexte:

Je reçois du code HTML brut d'un troisième serveur, ce que je souhaite

  • insérer dans mon application React
  • le modifier

L'approche JS vanille

  • Je peux modifier la chaîne avec regex et ajouter n'importe quelle balise HTML avec un id
  • Ensuite, je peux modifier ces éléments via getElementById, comme d'habitude

L'approche React

  • Je ne devrais pas utiliser le DOM
  • Ensuite, je devrais insérer dans la chaîne des composants qui ont une référence React
  • Le contraire (pour insérer certains composants React au format HTML brut) serait via ReactDOMServer.renderToString
  • Ainsi, lorsque j'injecte les composants avec ReactDOM.render(), le problème est que la méthode render prend son temps, de sorte que si dans la ligne suivante j'essaye d'utiliser la référence qui existe dans le composant inséré, c'est pas encore là

La question

  • Comment faire? Habituellement, je mettrais le code dans un useEffect avec des dépendances [], mais ici je suis rendering le composant lorsque l'application est déjà montée
  • Une solution de contournement rapide consiste simplement à faire une attente asynchrone de 500 ms, puis je pourrai accéder au ref, mais bien sûr, il doit y avoir quelque chose de mieux

Ce code échoue, car lorsque le ref est rendu, il n'est toujours pas disponible, donc ref.current n'est pas défini

Comment puis-je l'attendre?

codesandbox

EDIT: je fournit le code qui fonctionne mais via DOM direct, ce qui, je suppose, devrait être évité

import React, { useRef, useEffect } from "react";
import ReactDOM from "react-dom";

export default function App() {
  const myref = useRef();

  useEffect(() => {
    const Com = () => <div ref={myref}>hello</div>;
    ReactDOM.render(<Com />, document.getElementById("container"));
    console.log(myref.current); // undefined
    document.getElementById('container').textContent = "direct DOM works"

   // the next line fails since the ref is not yet available
   // myref.current.textContent = "but this REF is not available"; // fails
  }, []);

  const plainhtml = '<div><div id="container"></div><div>some more content</div><div id="another">even more content</div></div>'; // this is some large HTML fetched from an external server

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <div dangerouslySetInnerHTML={{ __html: plainhtml }} />
    </div>
  );
}
0
GWorking 24 avril 2020 à 15:15

2 réponses

Meilleure réponse

J'ai besoin d'utiliser un référence de rappel mais en l'encapsulant au sein de useCallback pour s'assurer qu'il ne se réémet qu'avec les dépendances indiquées (c'est-à-dire aucune []), de sorte qu'il ne soit exécuté que lorsque le composant change (comme expliqué ici)

codesandbox

import React, { useEffect, useCallback } from "react";
import ReactDOM from "react-dom";

export default function App() {
  const measuredRef = useCallback(node => {
    if (node !== null) {
      node.textContent = "useCallback DOM also works";
    }
  }, []);

  useEffect(() => {
    const Com = () => <div ref={measuredRef}>hello</div>;
    ReactDOM.render(<Com />, document.getElementById("container"));
    document.getElementById("container").textContent = "direct DOM works";
  }, []);

  const plainhtml = '<div id="container"></div>';

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <div dangerouslySetInnerHTML={{ __html: plainhtml }} />
    </div>
  );
}
0
GWorking 24 avril 2020 à 14:27

useEffect avec un tableau de dépendances vide exécute après le premier rendu, donc vous obtiendrez la référence DOM dans le rappel:

const htmlString = '<div id="container">Hello</div>';

export default function App() {
  const myRef = useRef();

  useEffect(() => {
    if (myRef.current) {
      myRef.current.textContent = 'whats up';
    }
    console.log(myRef.current);
  }, []);

  return (
    <div>
      <div ref={myRef} dangerouslySetInnerHTML={{ __html: htmlString }} />
      <div dangerouslySetInnerHTML={{ __html: htmlString }} />
    </div>
  );
}

/* App renders:
whats up
Hello
*/

Edit nervous-glade-8rtxb

0
Dennis Vash 24 avril 2020 à 13:03