Shiny observeEvent, observe et eventReactive : gérer les événements en R

Exécutez du code au clic d’un bouton, réagissez à une entrée précise, ou calculez une valeur au clic sur Go — et sachez quel observateur choisir.

Programming
Shiny

Une entrée qui pilote une sortie est automatique, mais les vraies applications ont aussi besoin de FAIRE des choses sur des événements : enregistrer au clic d’un bouton, synchroniser une liste déroulante, exécuter un travail coûteux seulement au clic sur Go. C’est le travail des observateurs. Apprenez observe() pour les effets de bord à tout changement, observeEvent() pour « faire X quand ceci se déclenche », eventReactive() pour une valeur calculée seulement au déclenchement, plus les déclencheurs multiples, ignoreInit/ignoreNULL et isolate().

Date de publication

28 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Les observateurs font des choses ; les valeurs réactives renvoient des valeurs. Quand vous voulez une action — enregistrer, notifier, mettre à jour une autre entrée — vous faites appel à un observateur, pas à un reactive().
  • observe() s’exécute dès que quoi que ce soit qu’il lit change. Aucune valeur de retour, aucun déclencheur à nommer ; il se déclenche à chaque dépendance. Puissant, mais facile à sur-déclencher — utilisez-le avec parcimonie.
  • observeEvent(trigger, { … }) ne s’exécute que lorsque trigger se déclenche. Le cheval de bataille du « faire X quand le bouton est cliqué » : il réagit au déclencheur et ignore tout le reste dans son corps.
  • eventReactive(trigger, { … }) renvoie une valeur calculée seulement lorsque le déclencheur se déclenche. Le patron « exécuter le travail coûteux au clic sur Go » — une valeur réactive que vous lisez avec ().
  • isolate() lit une valeur réactive sans en dépendre, et observeEvent() accepte plusieurs déclencheurs à la fois — les deux contrôles dont vous avez besoin pour une gestion précise des événements.

Introduction

Un curseur qui redessine un graphique, c’est la réactivité en pilote automatique — vous déclarez la dépendance et Shiny réexécute la sortie pour vous. Mais les applications ont aussi besoin d’agir sur des événements qui ne sont pas « une sortie qui lit une entrée ». Enregistrer les résultats quand l’utilisateur clique sur Enregistrer. Afficher une notification. Exécuter un modèle lent seulement quand il clique sur Go. Garder les choix d’une liste déroulante synchronisés avec le jeu de données choisi. Aucune de ces actions ne remplit un emplacement de sortie — ce sont des effets de bord et des calculs conditionnés, et c’est le travail des observateurs.

La programmation réactive a présenté observe(), observeEvent() et eventReactive() en un paragraphe chacun. Cette leçon en est le traitement dédié : ce que chacun fait réellement, les commutateurs ignoreInit/ignoreNULL qui font trébucher, comment gérer plusieurs déclencheurs, et isolate() pour lire une valeur sans en prendre la dépendance. Avec les valeurs réactives vous avez appris l’état que vous écrivez ; ici vous apprenez le code qui fait cette écriture.

NoteCopiez n’importe quel bloc dans app.R et exécutez-le en local

Le code de cette page n’est pas exécuté ici — une application Shiny a besoin d’une session R active, elle ne peut donc pas tourner dans une page web statique. Copiez n’importe quel bloc dans un fichier app.R et exécutez-le en local avec shiny::runApp() (ou cliquez sur Run App dans RStudio). Chaque extrait se dépose dans la fonction server d’une application.

Des effets de bord, pas des valeurs

La distinction qui organise tout ce qui suit : un reactive() ou un eventReactive() renvoie une valeur que vous appelez et réutilisez ; un observe() ou un observeEvent() fait un effet de bord et ne renvoie rien. Vous n’appelez jamais un observateur — il s’exécute tout seul quand ses dépendances changent.

total  <- reactive({ sum(input$values) })   # a VALUE you call: total()
observe({ cat("inputs changed\n") })        # a SIDE EFFECT: prints, returns nothing

Si ce que vous voulez est « calculer X pour qu’une sortie puisse l’afficher », c’est une valeur réactive (ou un render*()). Si ce que vous voulez est « faire que X arrive » — écrire un fichier, afficher une notification, mettre à jour un autre contrôle, changer votre propre état — c’est un observateur. Gardez cette distinction et la plupart des choix ci-dessous se font d’eux-mêmes.

observe() : réagir à tout changement

observe() exécute son corps dès que n’importe quelle valeur réactive qu’il lit change. Il n’y a aucun déclencheur à nommer et rien à renvoyer — il existe uniquement pour l’effet de bord. L’usage classique est de garder un élément de l’UI synchronisé avec un autre.

observe({
  # re-runs whenever input$dataset changes
  updateSelectInput(session, "column", choices = names(get(input$dataset)))
})

Parce qu’il suit tout ce qu’il lit, observe() est le bon outil pour « garder ceci synchronisé avec ce qui a changé » — un état d’UI dérivé, une liste déroulante dépendante, une ligne de statut. Mais cette même portée est le piège : lisez trois entrées dans un seul observe() et il se déclenche sur les trois, même quand vous ne vous souciiez que d’une seule. Utilisez-le avec parcimonie, et dès que vous vous surprenez à penser « mais seulement quand ceci change », passez à observeEvent().

observeEvent() : faire X quand un déclencheur se déclenche

observeEvent() est l’observateur ciblé et celui auquel vous ferez appel le plus souvent. Vous lui donnez un déclencheur — généralement un bouton — et un corps ; le corps s’exécute seulement quand le déclencheur se déclenche, et les autres valeurs réactives qu’il lit à l’intérieur ne le redéclenchent pas.

observeEvent(input$save, {
  saveRDS(results(), "results.rds")
  showNotification("Saved.", type = "message")
})

C’est le patron « faire X au clic ». Le corps lit results(), mais un changement de results() ne le déclenchera pas — seul un clic sur input$save le fera. Cette isolation est exactement ce que vous voulez : l’utilisateur choisit ses options librement, et rien ne se passe tant qu’il n’a pas pressé le bouton.

Deux commutateurs contrôlent son comportement aux limites :

  • ignoreInit = TRUE — ne pas se déclencher au démarrage de l’application. Par défaut observeEvent() s’exécute une fois quand l’application se charge (la valeur initiale du déclencheur compte comme un « changement »). Pour un bouton cela n’a pas d’importance — un actionButton neuf démarre à 0 et ignoreNULL le supprime déjà — mais quand le déclencheur est une entrée ordinaire (un selectInput, disons) le bloc se déclenche au chargement avant que l’utilisateur n’ait touché à quoi que ce soit. Mettez ignoreInit = TRUE pour ne réagir qu’aux vrais changements de l’utilisateur.
  • ignoreNULL = FALSE — se déclencher aussi quand le déclencheur vaut NULL/0. Par défaut observeEvent() ignore un déclencheur NULL ou un 0 initial (de sorte que le 0 de démarrage d’un bouton ne le déclenche pas). Mettez ignoreNULL = FALSE seulement quand vous voulez vraiment que le cas vide/initial s’exécute.
observeEvent(input$mode, {
  switch_layout(input$mode)
}, ignoreInit = TRUE)         # react to user changes, not the page-load value

Pour les boutons d’action vous touchez rarement à l’un ou l’autre commutateur — les valeurs par défaut sont les bonnes. Ils comptent surtout quand le déclencheur est une entrée ordinaire et que vous êtes surpris par un déclenchement au démarrage.

eventReactive() : une valeur calculée seulement au déclenchement

Parfois le déclencheur devrait produire une valeur, pas un effet de bord — « exécuter ce calcul coûteux quand Go est cliqué, puis laisser plusieurs sorties lire le résultat ». C’est eventReactive() : il ressemble à observeEvent() (un déclencheur, puis un corps), mais il renvoie une valeur réactive que vous lisez avec (), recalculée seulement quand le déclencheur se déclenche et mise en cache entre-temps.

result <- eventReactive(input$go, {
  expensive_model(input$dataset, input$params)   # runs ONLY when input$go fires
})

output$summary <- renderPrint({ result() })       # read it with ()

L’utilisateur peut changer input$dataset et input$params autant qu’il veut — rien ne s’exécute tant qu’il ne clique pas sur Go, et quand il le fait, le résultat est mis en cache de sorte que chaque sortie lisant result() partage un seul calcul. C’est la différence avec un simple reactive(), qui recalculerait avidement à chaque changement d’entrée. Faites appel à eventReactive() chaque fois qu’une valeur coûteuse devrait être conditionnée derrière un bouton.

observe vs observeEvent vs eventReactive vs reactive() : lequel et quand

Quatre outils, deux questions. D’abord : effet de bord ou valeur ? Puis : réagir à tout ce qu’il lit, ou à un seul déclencheur nommé ?

Outil Renvoie une valeur ? S’exécute quand… À utiliser quand…
reactive() Oui — appel x() une entrée qu’il lit change une valeur calculée partagée par ≥2 sorties / une étape d’une chaîne
eventReactive(go, …) Oui — appel r() le déclencheur go se déclenche une valeur coûteuse conditionnée derrière un bouton « Go »
observe() Non (effet de bord) tout ce qu’il lit change garder quelque chose synchronisé avec ce qui a changé
observeEvent(go, …) Non (effet de bord) le déclencheur go se déclenche faire une action sur un bouton/événement (enregistrer, soumettre, réinitialiser)

La décision est rapide. Avez-vous besoin du résultat en retour ? Oui → une valeur réactive (reactive() s’il suit les entrées, eventReactive() si un bouton le conditionne). Non, vous voulez juste que quelque chose arrive → un observateur (observe() s’il doit tout suivre, observeEvent() si un seul déclencheur le pilote). En pratique observeEvent() et eventReactive() couvrent la plupart des applications réelles ; observe() est pour les vrais cas « synchroniser avec n’importe quoi ».

Plusieurs déclencheurs et isolate()

Deux patrons complètent la gestion des événements.

Plusieurs déclencheurs dans un seul observateur. Passez un vecteur ou une liste comme premier argument et le corps se déclenche quand l’un quelconque d’entre eux change — pratique quand deux boutons différents devraient faire la même chose, ou qu’une réinitialisation devrait répondre à plusieurs contrôles.

observeEvent(list(input$apply, input$refresh), {
  reload_data()                     # fires when EITHER button is clicked
})

Lire une valeur sans en dépendre. À l’intérieur d’un observateur vous avez souvent besoin de la valeur courante d’une entrée sans en faire un déclencheur. isolate() lit la valeur d’une valeur réactive mais n’en prend aucune dépendance, de sorte qu’un changement de cette entrée ne redéclenchera pas le bloc.

observeEvent(input$submit, {
  # fires on input$submit only; reads the note's CURRENT value without making it a trigger
  save_entry(text = isolate(input$note), at = Sys.time())
})

Ici seul un clic sur input$submit exécute le bloc ; isolate(input$note) saisit ce qui se trouve dans le champ de note à ce moment-là sans transformer chaque frappe en déclencheur. (Avec observeEvent() les lectures du corps ne le redéclenchent déjà pas, donc isolate() est surtout utile à l’intérieur d’un simple observe() ou d’un reactive() — mais c’est la façon explicite de dire « lis ceci maintenant, ne le surveille pas ».)

Problèmes fréquents

Le corps de votre observeEvent « renvoie » une valeur que vous comptiez utiliser — mais rien ne la lit. Un observateur est un effet de bord ; tout ce que son corps évalue est jeté. Si vous avez écrit observeEvent(input$go, { expensive_model(...) }) puis essayé de lire ce résultat ailleurs, il n’y a rien à lire. Quand vous avez besoin de la valeur, utilisez eventReactive(input$go, { expensive_model(...) }) et appelez-la avec result() — c’est le frère qui renvoie une valeur.

Un observe() se déclenche bien plus souvent que vous ne le vouliez. Parce qu’il suit chaque valeur réactive dans son corps, lire trois entrées le fait se réexécuter sur les trois. Si vous ne vous souciez que d’une seule — « faire ceci quand le bouton est cliqué » — c’est observeEvent(input$go, { … }), qui réagit au déclencheur et ignore le reste. Réservez observe() aux vrais cas « réagir à tout ce qui a changé ».

Un bloc se déclenche au démarrage alors que vous ne le vouliez pas. observeEvent() s’exécute une fois au chargement de l’application par défaut (la valeur initiale du déclencheur compte comme un changement). Pour un bouton c’est sans conséquence ; pour un déclencheur d’entrée ordinaire cela peut exécuter votre code avant que l’utilisateur n’agisse. Ajoutez ignoreInit = TRUE pour ne réagir qu’aux vrais changements — et rappelez-vous que ignoreNULL vaut TRUE par défaut, donc le 0 de démarrage d’un bouton est déjà supprimé sauf si vous le basculez.

Questions fréquentes

observe() exécute son corps dès que n’importe quelle valeur réactive qu’il lit change — il n’y a aucun déclencheur à nommer, donc c’est pour « réagir à tout ce qui a changé » (garder une liste déroulante synchronisée, journaliser). observeEvent(trigger, { … }) ne s’exécute que lorsque trigger se déclenche (généralement un bouton) et ignore les autres valeurs que son corps lit. Utilisez observe() pour une synchronisation large, observeEvent() pour « faire X quand cette chose précise se produit ».

Utilisez eventReactive() quand le déclencheur devrait produire une valeur que les sorties lisent, pas seulement un effet de bord. Les deux attendent un déclencheur, mais observeEvent() ne renvoie rien (il agit — enregistre, notifie), tandis qu’eventReactive(input$go, { … }) renvoie une valeur réactive mise en cache que vous lisez avec result(). Faites appel à eventReactive() pour conditionner un calcul coûteux derrière un bouton « Go » et partager le résultat entre les sorties.

Ajoutez un actionButton("go", "Go") à l’UI et un observeEvent(input$go, { … }) au server. Le corps ne s’exécute que lorsque le bouton est cliqué ; les autres entrées qu’il lit à l’intérieur ne le redéclenchent pas. Si vous avez besoin que le clic produise une valeur pour les sorties plutôt qu’un effet de bord, utilisez eventReactive(input$go, { … }) à la place et lisez-la avec ().

ignoreInit = TRUE empêche le bloc de se déclencher une fois au démarrage de l’application (par défaut il s’exécute au chargement parce que la valeur initiale du déclencheur compte comme un changement) — mettez-le quand le déclencheur est une entrée ordinaire et que vous ne voulez que les vrais changements de l’utilisateur. ignoreNULL (par défaut TRUE) supprime le déclenchement quand le déclencheur vaut NULL ou le 0 initial d’un bouton ; mettez ignoreNULL = FALSE seulement quand vous voulez aussi que le cas vide/initial s’exécute.

Passez une liste ou un vecteur de déclencheurs comme premier argument : observeEvent(list(input$apply, input$refresh), { … }). Le corps se déclenche quand l’un quelconque d’entre eux change — utile quand deux boutons devraient faire la même chose. Pour lire la valeur courante d’une entrée à l’intérieur d’un observateur sans en faire un déclencheur, enveloppez-la dans isolate().

Testez vos connaissances

Une application a deux entrées (input$dataset, input$params) et un bouton actionButton("run", "Run report"). Une fonction lente build_report(dataset, params) produit une valeur que deux sorties affichent toutes les deux. Pour l’instant l’appel lent se trouve à l’intérieur de chaque render, il s’exécute donc deux fois à chaque changement d’entrée. Réécrivez le server pour que le rapport (1) soit calculé une seule fois et partagé par les deux sorties, et (2) ne s’exécute que quand input$run est cliqué, pas à chaque changement d’entrée.

Vous avez besoin d’une valeur unique, partagée entre les sorties, qui ne se recalcule que sur un bouton. Un simple reactive() partage mais recalcule avidement ; vous voulez la version conditionnée par un bouton. Faites appel à eventReactive(input$run, { … }) — il renvoie une valeur réactive mise en cache (appelez-la avec ()) qui ne s’exécute que lorsque le déclencheur se déclenche.

server <- function(input, output, session) {

  # runs ONCE per click of input$run, then caches; shared by both outputs
  report <- eventReactive(input$run, {
    build_report(input$dataset, input$params)
  })

  output$plot  <- renderPlot({  plot(report()) })
  output$table <- renderTable({ report()$summary })
}

eventReactive(input$run, …) vous donne une valeur conditionnée et mise en cache : build_report() ne s’exécute que lorsque Run report est cliqué, et les deux sorties appellent report() pour réutiliser le même résultat — donc le travail lent se fait une fois par clic au lieu de deux fois par frappe.

Vous voulez enregistrer les résultats courants sur le disque quand l’utilisateur clique sur un bouton Enregistrer — aucune valeur ne revient, vous voulez juste que l’enregistrement ait lieu. Quel outil convient, et qu’est-ce qui le déclenche ?

A. reactive({ saveRDS(results(), "out.rds") }) — appelez-le pour enregistrer. B. observe({ saveRDS(results(), "out.rds") }) — s’exécute à chaque changement. C. observeEvent(input$save, { saveRDS(results(), "out.rds") }) — s’exécute au clic. D. eventReactive(input$save, { saveRDS(results(), "out.rds") }) — lisez-le avec ().

C. L’enregistrement est un effet de bord sans valeur à renvoyer, et il devrait se produire sur un événement précis, donc observeEvent(input$save, { … }) est exactement le bon choix — il se déclenche au clic et ignore les autres valeurs que son corps lit. A est faux : un reactive() renvoie une valeur et est paresseux, donc il ne s’exécutera pas comme une action. B réenregistrerait à chaque changement de quoi que ce soit qu’il lit, pas seulement au clic. D renvoie une valeur dont personne n’a besoin et, étant une valeur réactive, ne s’exécutera pas tant que quelque chose ne l’appelle pas.

Conclusion

Les observateurs sont la façon dont un server Shiny agit sur les événements. Gardez en tête la distinction valeur-versus-effet-de-bord : reactive() et eventReactive() renvoient des valeurs que vous appelez ; observe() et observeEvent() font des effets de bord et ne renvoient rien. Faites appel à observeEvent(trigger, { … }) pour exécuter une action quand une chose se déclenche — le cheval de bataille des boutons — et à eventReactive(trigger, { … }) quand ce déclencheur devrait produire une valeur conditionnée et partagée. Gardez observe() pour les vrais cas « synchroniser avec n’importe quoi », faites attention à ignoreInit/ignoreNULL quand le déclencheur est une entrée ordinaire, et utilisez isolate() (ou une liste de déclencheurs) pour un contrôle précis de ce qui déclenche un bloc.

Avec la réactivité, l’état et la gestion des événements en main, l’étape suivante est de façonner les données sur lesquelles ces événements travaillent — les charger, les filtrer et les gérer proprement dans le server.

Leçons connexes

Cette page vous a-t-elle été utile ?

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Shiny observeEvent, observe et eventReactive : gérer les
    événements en R},
  date = {2026-06-28},
  url = {https://www.datanovia.com/learn/programming/shiny/server/observe-events},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Shiny observeEvent, observe et eventReactive : gérer les événements en R.” 2026. June 28. https://www.datanovia.com/learn/programming/shiny/server/observe-events.