{"id":10330,"date":"2018-08-17T09:10:14","date_gmt":"2018-08-17T14:10:14","guid":{"rendered":"http:\/\/carterembry.com\/\/?p=10330"},"modified":"2025-08-05T09:05:23","modified_gmt":"2025-08-05T14:05:23","slug":"creating-flexible-and-reusable-react-file-uploaders","status":"publish","type":"post","link":"https:\/\/carterembry.com\/fr\/2018\/creating-flexible-and-reusable-react-file-uploaders\/%20","title":{"rendered":"Cr\u00e9er des t\u00e9l\u00e9chargeurs de fichiers React flexibles et r\u00e9utilisables"},"content":{"rendered":"<div id=\"pl-10330\"  class=\"panel-layout\" ><div id=\"pg-10330-0\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-0-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-0-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"0\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Daniel a r\u00e9cemment fait l'objet d'un article sur le blog d'ing\u00e9nierie d'Eventbrite et nous sommes tr\u00e8s fiers du travail qu'il y accomplit.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-1\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-1-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-1-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"1\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/Flexible-Reusable-React-File-Uploader.png\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/Flexible-Reusable-React-File-Uploader.png\" width=\"538\" height=\"300\" sizes=\"(max-width: 538px) 100vw, 538px\" title=\"\t\t\t\t\t\t\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-1-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-1-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"2\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>L'\u00e9quipe de cr\u00e9ation d'\u00e9v\u00e9nements chez Eventbrite avait besoin d'un t\u00e9l\u00e9chargeur d'images bas\u00e9 sur React qui offrirait de la flexibilit\u00e9 tout en pr\u00e9sentant une interface utilisateur simple. Les composants de l'uploader d'images devraient fonctionner dans une vari\u00e9t\u00e9 de sc\u00e9narios en tant que parties r\u00e9utilisables qui pourraient \u00eatre compos\u00e9es diff\u00e9remment selon les besoins. Lis la suite pour voir comment nous avons r\u00e9solu ce probl\u00e8me.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-2\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-2\" ><div id=\"pgc-10330-2-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-2-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"3\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Qu'est-ce qu'un t\u00e9l\u00e9chargeur de fichiers ?<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-3\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-3\" ><div id=\"pgc-10330-3-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-3-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"4\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Dans le pass\u00e9, si tu voulais obtenir un fichier de tes utilisateurs web, tu devais utiliser un\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/input\/file\">\"Type d'entr\u00e9e \"fichier<\/a>. Cette approche \u00e9tait limit\u00e9e \u00e0 bien des \u00e9gards, notamment en ce qu'elle ne permettait pas d'atteindre les objectifs fix\u00e9s.\u00a0<i>c'est une entr\u00e9e<\/i>: les donn\u00e9es ne sont transmises que lorsque tu soumets le formulaire, les utilisateurs n'ont donc pas la possibilit\u00e9 de voir les commentaires avant ou pendant le t\u00e9l\u00e9chargement.<\/p>\n<p>En gardant cela \u00e0 l'esprit, les t\u00e9l\u00e9chargeurs React dont nous allons parler ne sont pas des entr\u00e9es de formulaire ; ce sont des \" outils de transport imm\u00e9diat. \" L'utilisateur choisit un fichier, l'uploader le transporte vers un serveur distant, puis re\u00e7oit une r\u00e9ponse avec un certain identifiant unique. Cet identifiant est alors imm\u00e9diatement associ\u00e9 \u00e0 un enregistrement de base de donn\u00e9es ou plac\u00e9 dans un champ de formulaire cach\u00e9.<\/p>\n<\/div>\n<\/div><\/div><\/div><div id=\"pgc-10330-3-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-3-1-0\" class=\"so-panel widget widget_sow-video panel-first-child panel-last-child\" data-index=\"5\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-video so-widget-sow-video-default-f379b959fc93-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-video-wrapper\">\n\t\t\t<video\n\t\t\tid=\"sow-player-1\" class=\"sow-video-widget\" preload=\"auto\" style=\"width:100%;height:100%;\" controls>\n\t\t\t\t\t\t\t<source type=\"video\/mp4\" src=\"https:\/\/carterembry.com\/wp-content\/uploads\/2022\/02\/uploaders.mp4\"\/>\n\t\t\t\t\t<\/video>\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-4\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-4-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-4-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"6\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Cette nouvelle strat\u00e9gie offre une grande flexibilit\u00e9 par rapport aux processus de t\u00e9l\u00e9chargement traditionnels. Par exemple, le fait de dissocier le transport de fichiers des soumissions de formulaires nous permet de\u00a0<strong>t\u00e9l\u00e9charger directement vers un espace de stockage tiers (comme Amazon S3) sans envoyer les fichiers via nos serveurs.<\/strong>.<\/p>\n<p>La contrepartie de cette flexibilit\u00e9 est la complexit\u00e9 ; les t\u00e9l\u00e9chargeurs de fichiers par glisser-d\u00e9poser sont des b\u00eates complexes. Nous avions \u00e9galement besoin que notre t\u00e9l\u00e9chargeur React soit simple et utilisable. Trouver un chemin pour offrir \u00e0 la fois flexibilit\u00e9 et facilit\u00e9 d'utilisation n'a pas \u00e9t\u00e9 une t\u00e2che facile.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-5\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-5-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-5-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"7\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Identifier les responsabilit\u00e9s<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>\u00c9tablir les responsabilit\u00e9s d'un t\u00e9l\u00e9chargeur semble facile... il t\u00e9l\u00e9charge, n'est-ce pas ? Bien s\u00fbr, mais il y a beaucoup d'autres choses \u00e0 faire pour que cela se produise :<\/p>\n<ul>\n<li>Il doit avoir une zone de d\u00e9p\u00f4t qui change en fonction de l'interaction de l'utilisateur. Si l'utilisateur fait glisser un fichier dessus, elle doit indiquer ce changement d'\u00e9tat.<\/li>\n<li>Et si nos utilisateurs ne peuvent pas faire glisser les fichiers ? Peut-\u00eatre ont-ils des probl\u00e8mes d'accessibilit\u00e9 ou peut-\u00eatre essaient-ils de t\u00e9l\u00e9charger depuis leur t\u00e9l\u00e9phone. Quoi qu'il en soit, notre t\u00e9l\u00e9chargeur doit afficher un s\u00e9lecteur de fichiers lorsque l'utilisateur clique ou tape.<\/li>\n<li>Il doit valider le fichier choisi pour s'assurer qu'il est d'un type et d'une taille acceptables.<\/li>\n<li>Une fois qu'un fichier est s\u00e9lectionn\u00e9, il devrait afficher un aper\u00e7u de ce fichier pendant qu'il est t\u00e9l\u00e9charg\u00e9.<\/li>\n<li>Il doit donner des informations significatives \u00e0 l'utilisateur pendant le t\u00e9l\u00e9chargement, comme une barre de progression ou un graphique de chargement qui indique que quelque chose est en train de se passer.<\/li>\n<li>De plus, que se passe-t-il en cas d'\u00e9chec ? Il doit afficher une erreur significative \u00e0 l'utilisateur pour qu'il sache qu'il doit r\u00e9essayer (ou abandonner).<\/li>\n<li>Oh, et il doit en fait t\u00e9l\u00e9charger le fichier.<\/li>\n<\/ul>\n<p>Ces responsabilit\u00e9s ne sont qu'une courte liste, mais tu vois l'id\u00e9e, elles peuvent devenir compliqu\u00e9es tr\u00e8s rapidement. De plus, en t\u00e9l\u00e9chargeant\u00a0<i>images<\/i>\u00a0est notre cas d'utilisation principal, il peut y avoir une vari\u00e9t\u00e9 de besoins en mati\u00e8re de t\u00e9l\u00e9chargement de fichiers. Si tu t'es donn\u00e9 beaucoup de mal pour comprendre le comportement glisser\/d\u00e9poser\/mettre en \u00e9vidence\/valider\/transporter\/succ\u00e8s\/\u00e9chec, pourquoi tout r\u00e9\u00e9crire lorsque tu as soudain besoin de t\u00e9l\u00e9charger des fichiers CSV pour ce seul rapport ?<\/p>\n<p>Alors, comment pouvons-nous structurer notre t\u00e9l\u00e9chargeur d'images React pour obtenir un maximum de flexibilit\u00e9 et de r\u00e9utilisation ?<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-6\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-6\" ><div id=\"pgc-10330-6-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-6-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"8\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">S\u00e9paration des pr\u00e9occupations<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-7\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-7\" ><div id=\"pgc-10330-7-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-7-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"9\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__02.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__02.jpeg\" width=\"1152\" height=\"414\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-7-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-7-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"10\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Dans le diagramme suivant, tu peux voir une vue d'ensemble de l'approche que nous voulons adopter. Ne t'inqui\u00e8te pas si cela te semble compliqu\u00e9 - nous allons approfondir chacun de ces composants ci-dessous pour voir les d\u00e9tails de leur but et de leur r\u00f4le.<\/p>\n<p>Notre biblioth\u00e8que de composants bas\u00e9e sur React ne devrait pas avoir \u00e0 savoir comment fonctionnent nos API. Garder cette logique s\u00e9par\u00e9e pr\u00e9sente l'avantage suppl\u00e9mentaire de la r\u00e9utilisabilit\u00e9 ; diff\u00e9rents produits ne sont pas enferm\u00e9s dans une seule API ou m\u00eame un seul style d'API. Au lieu de cela, ils peuvent r\u00e9utiliser autant ou aussi peu que n\u00e9cessaire.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-8\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-8-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-8-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"11\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>M\u00eame au sein de\u00a0<a href=\"https:\/\/medium.com\/@dan_abramov\/smart-and-dumb-components-7ca2f9a7c7d0\">\u00e9l\u00e9ments de pr\u00e9sentation<\/a>Il est donc possible de s\u00e9parer la fonction de la pr\u00e9sentation. Nous avons donc pris notre liste de responsabilit\u00e9s et cr\u00e9\u00e9 une pile de composants, allant du plus g\u00e9n\u00e9ral en bas au plus sp\u00e9cifique en haut.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-9\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-9\" ><div id=\"pgc-10330-9-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-9-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"12\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Composantes fondamentales<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>UploaderDropzone<\/h4>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-10\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-10\" ><div id=\"pgc-10330-10-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-10-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"13\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__03.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__03.jpeg\" width=\"1152\" height=\"413\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-10-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-10-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"14\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Ce composant est le c\u0153ur de l'interface utilisateur du t\u00e9l\u00e9chargeur, l\u00e0 o\u00f9 l'action commence. Il g\u00e8re les \u00e9v\u00e9nements de glisser\/d\u00e9poser ainsi que le clic pour naviguer. Il n'a pas d'\u00e9tat lui-m\u00eame, il sait seulement comment normaliser et r\u00e9agir (tu vois ce que j'ai fait l\u00e0 ?) \u00e0 certaines actions de l'utilisateur. Il accepte les rappels en tant qu'accessoires afin de pouvoir dire \u00e0 son impl\u00e9menteur quand les choses se produisent.<\/p>\n<p>Il attend que des fichiers soient gliss\u00e9s sur lui, puis invoque un rappel. De m\u00eame, lorsqu'un fichier est choisi, soit par glisser\/d\u00e9poser, soit par cliquer pour naviguer, il invoque un autre rappel avec l'objet JS file.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-11\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-11-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-11-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"15\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Elle poss\u00e8de une de ces entr\u00e9es de type \"fichier\" dont j'ai parl\u00e9 plus haut, cach\u00e9e \u00e0 l'int\u00e9rieur pour les utilisateurs qui ne peuvent pas (ou pr\u00e9f\u00e8rent ne pas) faire glisser des fichiers. Cette fonctionnalit\u00e9 est importante, et en l'abstrayant ici, les composants qui utilisent la zone de d\u00e9p\u00f4t n'ont pas \u00e0 r\u00e9fl\u00e9chir \u00e0 la fa\u00e7on dont le fichier a \u00e9t\u00e9 choisi.<\/p>\n<p>Ce qui suit est un exemple d'UploaderDropzone utilisant React :<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-12\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-12\" ><div id=\"pgc-10330-12-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-12-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"16\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\nFais glisser un fichier ici !\n\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-12-0-1\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"17\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>UploaderDropzone n'a que tr\u00e8s peu d'avis sur son apparence, et n'a donc qu'un style minimal. Par exemple,\u00a0<a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=656164\">Certains navigateurs traitent les \u00e9v\u00e9nements de d\u00e9placement diff\u00e9remment<\/a>\u00a0lorsqu'ils se produisent sur des descendants profonds du n\u0153ud cible. Pour r\u00e9soudre ce probl\u00e8me, la dropzone utilise une seule div transparente pour couvrir tous ses descendants. Cela permet d'offrir l'exp\u00e9rience n\u00e9cessaire aux utilisateurs qui glissent\/d\u00e9posent, mais aussi aux utilisateurs de la zone de d\u00e9p\u00f4t.\u00a0<strong>maintient l'accessibilit\u00e9 pour les lecteurs d'\u00e9cran et autres technologies d'assistance.<\/strong><\/p>\n<\/div>\n<\/div><\/div><\/div><div id=\"pgc-10330-12-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-12-1-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"18\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__04.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__04.jpeg\" width=\"725\" height=\"458\" sizes=\"(max-width: 725px) 100vw, 725px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-13\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-13\" ><div id=\"pgc-10330-13-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-13-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"19\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__05.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__05.jpeg\" width=\"761\" height=\"471\" sizes=\"(max-width: 761px) 100vw, 761px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-13-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-13-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"20\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>UploaderLayoutManager<\/h4>\n<p>Le composant UploaderLayoutManager g\u00e8re la plupart des transitions d'\u00e9tat et sait quelle disposition doit \u00eatre affich\u00e9e pour chaque \u00e9tape du processus, tout en acceptant d'autres composants React comme accessoires pour chaque \u00e9tape.<\/p>\n<p><strong>Cela permet aux responsables de la mise en \u0153uvre de penser \u00e0 chaque \u00e9tape comme \u00e0 une id\u00e9e visuelle distincte sans se pr\u00e9occuper de savoir comment et quand chaque transition se produit<\/strong>. Les partisans de ce composant React doivent uniquement r\u00e9fl\u00e9chir \u00e0 la disposition qui doit \u00eatre visible \u00e0 un moment donn\u00e9 en fonction de l'\u00e9tat, et non \u00e0 la mani\u00e8re dont les fichiers sont aliment\u00e9s ou \u00e0 l'aspect de la disposition.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-14\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-14-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-14-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"21\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Voici une liste d'\u00e9tapes qui peuvent \u00eatre fournies au LayoutManager en tant qu'accessoires :<\/p>\n<ul>\n<li>\u00c9tapes g\u00e9r\u00e9es par LayoutManager :\n<ul>\n<li>Non remplie - une zone de d\u00e9p\u00f4t vide avec un appel \u00e0 l'action (\" T\u00e9l\u00e9chargez une belle image ! \").<\/li>\n<li>Fichier gliss\u00e9 sur la fen\u00eatre mais pas sur la zone de d\u00e9p\u00f4t (\"D\u00e9pose ce fichier ici !\").<\/li>\n<li>Fichier gliss\u00e9 sur la zone de d\u00e9p\u00f4t (\"D\u00e9pose-le maintenant !\")<\/li>\n<li>T\u00e9l\u00e9chargement de fichier en cours (\"Attends, je l'envoie...\")<\/li>\n<\/ul>\n<\/li>\n<li>\u00c9tape g\u00e9r\u00e9e par un composant qui impl\u00e9mente LayoutManager :\n<ul>\n<li>Le fichier a \u00e9t\u00e9 t\u00e9l\u00e9charg\u00e9 et est rempli. Pour notre t\u00e9l\u00e9chargeur d'images, il s'agit d'un aper\u00e7u de l'image avec un bouton \"Supprimer\".<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"22\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Le LayoutManager lui-m\u00eame n'a que peu ou pas de styles, et n'affiche que les \u00e9l\u00e9ments visuels qui lui ont \u00e9t\u00e9 transmis en tant qu'accessoires. Il est charg\u00e9 de g\u00e9rer l'\u00e9tape du processus atteinte par l'utilisateur et d'afficher le contenu de cette \u00e9tape.<\/p>\n<p>La seule \u00e9tape de la mise en page qui est g\u00e9r\u00e9e de mani\u00e8re externe est \"Aper\u00e7u\" (si l'Uploader a une image remplie). En effet, le composant de mise en \u0153uvre doit d\u00e9finir l'\u00e9tat dans lequel le t\u00e9l\u00e9chargeur d\u00e9marre. Par exemple, si l'utilisateur a d\u00e9j\u00e0 t\u00e9l\u00e9charg\u00e9 une image, nous voulons afficher cette image lorsqu'il revient sur la page.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-2\" class=\"so-panel widget widget_sow-editor\" data-index=\"23\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Exemple d'utilisation de LayoutManager :<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-3\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"24\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;uploaderlayoutmanager\n    dropzoneElement={}\n    windowDragDropzoneElement={}\n    dragDropzoneElement={}\n    loadingElement={}\n    previewElement={}\n \n    showPreview={!!fichier}\n \n    onReceiveFile={handleReceiveFile}\n\/&gt;\n<\/pre>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-15\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-15\" ><div id=\"pgc-10330-15-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-15-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"25\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Composants sp\u00e9cifiques aux ressources<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>Chargeur d'images<\/h4>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-16\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-16\" ><div id=\"pgc-10330-16-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-16-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"26\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__06.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__06.jpeg\" width=\"1152\" height=\"414\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-16-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-16-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"27\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Le composant ImageUploader est presque enti\u00e8rement orient\u00e9 vers la pr\u00e9sentation ; il d\u00e9finit l'aspect et la convivialit\u00e9 de chaque \u00e9tape et les transmet en tant qu'accessoires \u00e0 un UploadLayoutManager. C'est \u00e9galement un endroit id\u00e9al pour effectuer la validation (type de fichier, taille du fichier, etc.).<\/p>\n<p>Les partisans de cet outil peuvent se concentrer presque enti\u00e8rement sur l'aspect visuel de l'uploader. Ce composant conserve tr\u00e8s peu de logique puisque les transitions d'\u00e9tat sont g\u00e9r\u00e9es par le UploaderLayoutManager.\u00a0<strong>Nous pouvons changer les visuels de fa\u00e7on fluide sans trop nous soucier d'endommager la fonction de l'uploader.<\/strong><\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-17\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-17-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-17-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"28\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Exemple ImageUploader :<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-17-0-1\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"29\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nconst DropzoneLayout = () =&amp;gt; (\n    &lt;p&gt;Fais glisser un fichier ici ou clique pour le parcourir&lt;\/p&gt;\n) ;\nconst DragDropzoneLayout = () =&amp;gt; (\n    &lt;p&gt;D&eacute;pose le fichier maintenant !&lt;\/p&gt;\n) ;\nconst LoadingLayout = () =&amp;gt; (\n    &lt;p&gt;Merci de patienter, le chargement est en cours...&lt;\/p&gt;\n) ;\nconst PreviewLayout = ({file, onRemove}) =&amp;gt; (\n    &lt;div&gt;\n        &lt;p&gt;Nom : {file.name}&lt;\/p&gt;\n        &lt;button onclick=&quot;{onRemove}&quot;&gt;Supprimer le fichier&lt;\/button&gt;\n    &lt;\/div&gt;\n) ;\nclass ImageUploader extends React.Component {\n    state = {file : undefined} ;\n \n    _handleRemove = () =&amp;gt; this.setState({file : undefined}) ;\n \n    _handleReceiveFile = (file) =&amp;gt; {\n        this.setState({file}) ;\n \n        return new Promise((resolve, reject) =&amp;gt; {\n            \/\/ t&eacute;l&eacute;charge le fichier !\n        })\n        .catch(() =&amp;gt; this.setState({file : undefined}))\n    }\n \n    render() {\n        let {file} = this.state ;\n        let preview ;\n \n        if (file) {\n            preview = (\n                &lt;previewlayout file=&quot;{file}&quot; onremove=&quot;{this._handleRemove}&quot;&gt;&lt;\/previewlayout&gt;\n            ) ;\n        }\n \n        return (\n            &lt;uploaderlayoutmanager\n                dropzoneelement=&quot;{&lt;DropzoneLayout&quot;&gt;&lt;\/uploaderlayoutmanager&gt;}\n                dragDropzoneElement={&lt;dragdropzonelayout&gt;&lt;\/dragdropzonelayout&gt;}\n                loadingElement={&lt;loadinglayout&gt;&lt;\/loadinglayout&gt;}\n                previewElement={preview}\n                showPreview={!!fichier}\n                onReceiveFile={this._handleReceiveFile}\n            \/&amp;gt;\n        ) ;\n    }\n} ;\n<\/pre>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-18\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-18\" ><div id=\"pgc-10330-18-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-18-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"30\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Couche sp\u00e9cifique \u00e0 l'application<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-19\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-19\" ><div id=\"pgc-10330-19-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-19-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"31\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__07.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__07.jpeg\" width=\"1152\" height=\"415\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-19-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-19-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"32\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>L'exemple ci-dessus pr\u00e9sente un aspect important qui n'a rien \u00e0 voir avec la pr\u00e9sentation : le transport de fichiers qui se produit dans _handleReceiveFile. Nous voulons que ce composant ImageUploader vive dans notre biblioth\u00e8que de composants et soit d\u00e9coupl\u00e9 du comportement sp\u00e9cifique de l'API, nous devons donc le supprimer. Heureusement, c'est aussi simple que d'accepter une fonction via props qui renvoie une promesse qui se r\u00e9sout lorsque le t\u00e9l\u00e9chargement est termin\u00e9.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-20\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-20-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-20-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"33\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n_handleReceiveFile(file) {\n    \/\/ pourrait faire la validation du fichier ici avant le transport. Si le fichier \u00e9choue \u00e0 la validation, renvoie une promesse rejet\u00e9e.\n    let {uploadImage} = this.props ;\n \n    this.setState({file}) ;\n \n    return uploadImage(file)\n        .catch(() =&gt; this.setState({file : undefined}))\n}\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"34\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Avec ce petit changement, ce m\u00eame t\u00e9l\u00e9chargeur d'images peut \u00eatre utilis\u00e9 pour une vari\u00e9t\u00e9 d'applications.\u00a0<strong>Une partie de ton application peut t\u00e9l\u00e9charger des images directement vers un tiers (comme Amazon S3), tandis qu'une autre peut les t\u00e9l\u00e9charger vers un serveur local dans un but et une manipulation totalement diff\u00e9rents, mais en utilisant la m\u00eame pr\u00e9sentation visuelle.<\/strong><\/p>\n<p>Et maintenant, parce que toute cette complexit\u00e9 est compartiment\u00e9e dans chaque composant, l'ImageUploader a une impl\u00e9mentation tr\u00e8s propre :<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-2\" class=\"so-panel widget widget_sow-editor\" data-index=\"35\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;imageuploader uploadImage={S3ImageUploadApi}&gt;&lt;\/imageuploader&gt;\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-3\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"36\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Gr\u00e2ce \u00e0 cette base, les applications peuvent utiliser le m\u00eame ImageUploader de diff\u00e9rentes mani\u00e8res. Nous avons apport\u00e9 la flexibilit\u00e9 que nous souhaitions tout en gardant l'API propre et simple. De nouveaux wrappers peuvent \u00eatre construits sur UploadLayoutManager pour g\u00e9rer d'autres types de fichiers ou de nouvelles mises en page.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-21\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-21\" ><div id=\"pgc-10330-21-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-21-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"37\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">En cl\u00f4ture<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Imagine des t\u00e9l\u00e9chargeurs d'images qui ont \u00e9t\u00e9 con\u00e7us pour chaque sc\u00e9nario, mais qui ne contiennent que quelques composants simples faits de balises de pr\u00e9sentation.  Ils peuvent chacun utiliser la m\u00eame fonctionnalit\u00e9 de t\u00e9l\u00e9chargement si cela a du sens, mais avec une pr\u00e9sentation totalement diff\u00e9rente. Ou inverser cette id\u00e9e, en utilisant les m\u00eames visuels de t\u00e9l\u00e9chargement mais avec des interfaces API totalement diff\u00e9rentes.<\/p>\n<p>De quelles autres fa\u00e7ons pourrais-tu utiliser ces composants de base ? Quels autres t\u00e9l\u00e9chargeurs construirais-tu ? Il n'y a pas de limites si tu prends le temps de construire des composants r\u00e9utilisables.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-21-0-1\" class=\"so-panel widget widget_sow-button-grid panel-last-child\" data-index=\"38\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button-grid so-widget-sow-button-grid-default-129192d28302-10330\"\n\t\t\t\n\t\t>\t<div class=\"sow-buttons-grid\">\n\t\t<div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/www.eventbrite.com\/engineering\/create-reusable-react-file-uploaders\/\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tTrouve l'article original sur Eventbrite Engineering\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/reactnewsletter.com\/issues\/130\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tEn vedette dans la lettre d'information React num\u00e9ro #130\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/react.statuscode.com\/issues\/102\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tEn vedette dans React Status Issue #102\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div>\t<\/div>\n\t<\/div><\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Daniel a r\u00e9cemment \u00e9t\u00e9 pr\u00e9sent\u00e9 sur le blog d'ing\u00e9nierie d'Eventbrite et nous sommes tr\u00e8s fiers du travail qu'il y accomplit. L'\u00e9quipe de cr\u00e9ation d'\u00e9v\u00e9nements chez Eventbrite avait besoin d'un t\u00e9l\u00e9chargeur d'images bas\u00e9 sur React qui offrirait de la flexibilit\u00e9 tout en pr\u00e9sentant une interface utilisateur simple. Les composants de l'uploader d'images devraient fonctionner dans une vari\u00e9t\u00e9 de sc\u00e9narios en tant qu'\u00e9l\u00e9ments r\u00e9utilisables [...]<\/p>","protected":false},"author":3,"featured_media":13450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wprm-recipe-roundup-name":"","wprm-recipe-roundup-description":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"default","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[10,5,7],"tags":[85,106,112,168,175,199,238,292],"class_list":["post-10330","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-daniel","category-professional-opinions","category-technical-jargon","tag-component","tag-engineering","tag-eventbrite","tag-image-uploader","tag-javascript","tag-nashville","tag-react","tag-uploader"],"_links":{"self":[{"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/posts\/10330","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/comments?post=10330"}],"version-history":[{"count":12,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/posts\/10330\/revisions"}],"predecessor-version":[{"id":21449,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/posts\/10330\/revisions\/21449"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/media\/13450"}],"wp:attachment":[{"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/media?parent=10330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/categories?post=10330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carterembry.com\/fr\/wp-json\/wp\/v2\/tags?post=10330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}