Découvrez comment contentEditable peut être utilisé avec React pour permettre un contenu interactif et modifiable
Vous êtes-vous déjà demandé comment les applications aiment Google Docs ou alors Notion retirer du contenu riche interactif et modifiable dans les applications Web ? L’approche habituelle pour créer des formulaires avec des entrées telles que textarea
ne peut pas offrir le même genre d’expérience.
Au lieu de cela, des applications comme celles-ci utilisent HTML contentEditable
attribut, qui permet à un utilisateur de modifier directement le contenu d’une page. Bien que l’activation de l’édition de contenu soit aussi simple que de définir le contentEditable
Cependant, l’intégrer dans une application et en extraire des données utilisables est une autre histoire.
En particulier, si vous avez essayé d’utiliser contentEditable dans Réagir, vous vous rendrez vite compte que les deux ne jouent pas bien ensemble, du moins, pas sans un peu de travail supplémentaire. Dans cet article, nous verrons comment contentEditable
peut être utilisé avec React pour activer le contenu interactif et modifiable dans vos applications React.
contentEditable
est un attribut qui peut être placé sur des éléments HTML pour rendre le contenu modifiable par l’utilisateur. Nous pouvons démontrer comment contentEditable
est utilisé avec l’extrait de code HTML suivant :
<div style="border: 1px solid #ccc; padding: 8px" contentEditable>
<h4>Editable Content - Edit Me!</h4>
<p>This piece of content has the contentEditable attribute set.</p>
</div>
Comme mentionné précédemment, l’activation de l’édition avec contentEditable
c’est simple mais pas toujours facile pour faire fonctionner votre application. Pour créer entièrement une application avec contentEditable
vous devez résoudre les problèmes suivants d’une manière ou d’une autre.
- Ajouter des gestionnaires de modifications pour le contenu mis à jour
- Ajouter la validation et le nettoyage du contenu édité
- Ajoutez une traduction vers et depuis votre représentation de données interne (la plupart des applications ne stockent pas de code HTML brut pour le contenu)
- Faites en sorte que le contenu modifiable s’harmonise avec le reste du cycle de vie des données de votre application
Ce dernier point est un challenge particulier avec React- contentEditable
et Réagir aux conflits dans la façon dont ils gèrent et mettent à jour l’état du DOM.
Avant de regarder contentEditable
avec React, regardons rapidement comment nous pourrions implémenter non riche édition de texte avec textarea
:
const TextAreaEditable = () => {
const [content, setContent] = React.useState("")
return (
<textarea value={content} onChange={e => setContent(e.currentTarget.value)} />
)
}
Mais si nous essayons de suivre ce modèle pour contentEditable
contenu dans une application React, nous commencerons rapidement à rencontrer des erreurs, des avertissements et des comportements inattendus. Jetons un coup d’œil à une tentative naïve d’adaptation du code ci-dessus pour contentEditable
:
🚫 Our naive attempt at using contentEditable: const Editable = () => {
const [content, setContent] = React.useState("")
return (
<div onBlur={t => setContent(t.currentTarget.innerHTML)} contentEditable>
{content}
</div>
)
}
Si nous regardons dans la console, nous voyons cet avertissement dès que nous affichons la page :
Avertissement : Un composant est
contentEditable
et contientchildren
géré par React. Il est maintenant de votre responsabilité de garantir qu’aucun de ces nœuds ne soit modifié ou dupliqué de manière inattendue. Ce n’est probablement pas intentionnel.
Le problème est qu’avec contentEditable
activé, React et le navigateur veulent gérer l’état du contenu à l’intérieur de l’élément. De plus, lorsque nous commencerons à modifier le contenu, nous rencontrerons d’autres problèmes.
Si je commence à modifier le contenu et que j’appuie sur Commande-B pour mettre en gras une partie du texte que je modifie, je remarquerai deux choses :
Tout d’abord – ça marche ! – sans faire de manipulation spéciale, nous sommes capables de faire le genre de manipulation de style de texte que nous attendons d’un éditeur interactif.
Mais la deuxième chose que je remarque, c’est que lorsque la vue se brouille et que la mise à jour de l’état se produit, mon texte se transforme en un gâchis de HTML ! Au lieu de voir le texte « 1 23″, je vois 1<span style="font-weight: bold;">2</span>3
Ce n’est pas ce que nous voulons !
Le problème, bien sûr, est que notre chaîne de contenu est du pur HTML, mais nous essayons de le rendre dans React en le passant comme enfant de div
. React n’interprète pas ces enfants comme HTML et échappe le contenu à la place.
Pour faire de notre contentEditable
composant fonctionne correctement, nous devons dire à React de restituer une chaîne HTML, au lieu de texte brut. Heureusement, une telle technique existe, mais elle porte un nom effrayant : dangerouslySetInnerHTML
.
Le dangerouslySetInnerHTML
L’attribut peut être utilisé pour définir le code HTML interne d’un élément, nous permettant de réécrire notre Editable
composant:
✅ Our second attempt at using contentEditable: const Editable = () => { const [content, setContent] = React.useState("") const onContentBlur = React.useCallback(evt => setContent(evt.currentTarget.innerHTML)) return ( <div contentEditable onBlur={onContentBlur} dangerouslySetInnerHTML={{__html: content} /> ) }
Comme le nom le suggère, dangerouslySetInnerHTML
est dangereux. Pourquoi ? Parce qu’une entrée HTML non nettoyée peut constituer un risque pour la sécurité et exposer les utilisateurs à des attaques de script intersite (XSS). Imaginez que nous utilisons contentEditable
pour permettre la collaboration entre les utilisateurs. Si nous ne faisons pas attention, un attaquant pourrait intégrer JavaScript dans le contenu qui sera exécuté dans le navigateur de la victime lorsque dangerouslySetInnerHTML
est utilisé.
À cause de cela, c’est toujours recommandé d’utiliser une certaine forme de désinfection des entrées lors de l’utilisation contentEditable
et dangerouslySetInnerHTML
. Le paquet sanitize-html
peut aider à appliquer un ensemble de balises HTML sur liste blanche pour empêcher les attaques XSS.
✅ Sanitized HTML with contentEditable: import sanitizeHtml from "sanitize-html"
const Editable = () => {
const [content, setContent] = React.useState("")
const onContentBlur = React.useCallback(evt => {
const sanitizeConf = {
allowedTags: ["b", "i", "a", "p"],
allowedAttributes: { a: ["href"] }
}
setContent(sanitizeHtml(evt.currentTarget.innerHTML, sanitizeConf))
}, [setContent])
return (
<div contentEditable onBlur={onContentBlur} dangerouslySetInnerHTML={{__html: content}} />
)
}
Notez que pour être sûr que votre application ne soit pas sujette aux attaques XSS, il ne suffit pas de valider uniquement lors de l’édition. Vous devez également assainir et valider le contenu côté serveur ou sur le contenu revenant du serveur ! Si vous désinfectez uniquement lorsqu’un utilisateur modifications contenu, un attaquant intelligent peut contourner les contrôles de sécurité et envoyer des données non sécurisées directement au serveur.
Alors que l’approche de base ci-dessus vous permet d’utiliser contentEditable
dans votre application React, vous remarquerez peut-être des cas extrêmes lorsque vous commencez à implémenter des fonctionnalités dans votre application. Plus précisément, si vous ajoutez des gestionnaires d’événements supplémentaires à votre div
pour capturer l’entrée avant l’événement de flou, vous pouvez voir des sauts de curseur et d’autres comportements indésirables. C’est parce qu’un peu de logique supplémentaire est nécessaire pour déterminer lorsque pour réellement modifier l’état du contenu et déclencher un nouveau rendu.
Bien que nous puissions ajouter une solution de contournement pour cela et d’autres cas extrêmes dans notre composant sans trop beaucoup de problèmes, pourquoi réinventer la roue ? Le react-contenteditable
package résout la plupart des problèmes courants que vous êtes susceptible de rencontrer contentEditable
dans une application React.
L’utilisation de ce package est un changement assez simple par rapport à notre exemple précédent :
✅ Sanitized HTML with the ContentEditable package: import sanitizeHtml from "sanitize-html"
import ContentEditable from 'react-contenteditable'
const Editable = () => {
const [content, setContent] = React.useState("")
const onContentChange = React.useCallback(evt => {
const sanitizeConf = {
allowedTags: ["b", "i", "a", "p"],
allowedAttributes: { a: ["href"] }
}
setContent(sanitizeHtml(evt.currentTarget.innerHTML, sanitizeConf))
}, [setContent])
return (
<ContentEditable onChange={onContentChange} onBlur={onContentChange} html={content} />
)
}
En utilisant sanitize-html
et react-contenteditable
nous pouvons rapidement créer les bases d’une édition de contenu riche et interactive dans une application React.
Dans cet article, nous avons appris un peu comment ajouter du contenu modifiable par l’utilisateur à votre application React. Les principales considérations sont :
contentEditable
dans React nécessite une manipulation particulière.- Le
react-contenteditable
package peut aider à gérer les cas extrêmes que vous pourriez rencontrer. - N’oubliez pas de nettoyer le HTML lors de l’édition et avant de rendre le contenu non fiable. Le
sanitize-html
package peut aider à cela.
En utilisant cette approche simple, vous pouvez commencer à ajouter du contenu modifiable à n’importe quelle application React !