React: Mettre en œuvre le chargement latéral des données

Créé le 13 mars 2015  ·  136Commentaires  ·  Source: facebook/react

Il s'agit d'une API de première classe pour le chargement latéral de données sans état (bien que potentiellement mémorisées) à partir d'un magasin/réseau/ressource global, utilisant potentiellement des accessoires/état comme entrée.

type RecordOfObservables = { [key:string]: Observable<mixed> };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return <div>{myContent}</div>;
  }

}

observe() s'exécute après componentWillMount/componentWillUpdate mais avant le rendu.

Pour chaque clé/valeur de l'enregistrement. Abonnez-vous à l'Observable dans la valeur.

subscription = observable.subscribe({ onNext: handleNext });

Nous permettons à onNext d'être invoqué de manière synchrone à partir de subscribe. Si c'est le cas, nous définissons :

this.data[key] = nextValue;

Sinon, nous le laissons indéfini pour le rendu initial. (Peut-être que nous le définissons sur null ?)

Ensuite, le rendu se déroule comme d'habitude.

Chaque fois que onNext est invoqué, nous planifions un nouveau "this.data[key]" qui déclenche effectivement une mise à jour forcée sur ce composant. S'il s'agit du seul changement, alors observe n'est pas réexécuté (componentWillUpdate -> render -> componentDidUpdate).

Si props / state a changé (c'est-à-dire une mise à jour de recieveProps ou setState), alors observe() est réexécuté (pendant la réconciliation).

À ce stade, nous parcourons le nouvel enregistrement et nous abonnons à tous les nouveaux Observables.

Après cela, désabonnez-vous des Observables précédents.

subscription.dispose();

Cet ordre est important car il permet au fournisseur de données de faire le comptage des références de son cache. C'est-à-dire que je peux mettre en cache des données aussi longtemps que personne ne les écoute. Si je me désinscrivais immédiatement, le nombre de références tomberait à zéro avant que je ne m'abonne à nouveau aux mêmes données.

Lorsqu'un composant est démonté, nous nous désinscrivons automatiquement de tous les abonnements actifs.

Si le nouvel abonnement n'a pas immédiatement appelé onNext, nous continuerons à utiliser la valeur précédente.

Donc, si mon this.props.url de mon exemple change et que je m'abonne à une nouvelle URL, myContent continuera d'afficher le contenu de l'url précédente jusqu'à ce que l'url suivante soit complètement chargée.

Cela a la même sémantique que la balise <img /> . Nous avons vu que, même si cela peut prêter à confusion et entraîner des incohérences, il s'agit d'une valeur par défaut assez saine, et il est plus facile de lui faire afficher un spinner que d'avoir la valeur par défaut opposée.

La meilleure pratique peut consister à envoyer immédiatement une valeur "null" si vous n'avez pas les données en cache. Une autre alternative consiste pour un Observable à fournir à la fois l'URL (ou l'ID) et le contenu dans le résultat.

class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

Nous devrions utiliser le contrat RxJS d'Observable car il est plus courant et permet une exécution synchrone, mais une fois que la proposition de

var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();

Nous pouvons ajouter plus de crochets de cycle de vie qui répondent à ces événements si nécessaire.

Remarque : Ce concept permet aux données latérales de se comporter comme des "comportements" - tout comme les accessoires. Cela signifie que nous n'avons pas à surcharger l'état de la notion pour ces choses. Il permet des optimisations telles que jeter les données uniquement pour se réabonner plus tard. Il est restaurable.

Component API Big Picture

Commentaire le plus utile

Si quelqu'un veut jouer avec ce type d'API, j'ai créé un polyfill vraiment stupide pour observe tant que composant d'ordre supérieur :

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

Usage:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Tous les 136 commentaires

undefined est probablement la valeur la plus sûre à attribuer à data jusqu'à ce que l'observable fournisse sa première valeur via onNext . Par exemple, dans Relay, nous attribuons des significations différentes à null (les données n'existent pas) et undefined (pas encore récupérées), donc notre valeur de données par défaut idéale serait undefined . L'alternative est de fournir une nouvelle méthode, par exemple getInitialData , mais je soupçonne que c'est inutile/exagéré.

C'est assez intéressant cependant du point de vue du typage statique, je ne suis pas si content du système clé/valeur, leur type est quasiment impossible à exprimer.
Pourquoi ne pas avoir observe retourner un seul observable et définir/fusionner la valeur résolue en this.data :

class Foo {

  observe() {
    return (
      loadUser(this.props.userID)
        .map(user => { user })
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }

}

Et pour le cas d'une récupération multiple, quelque chose comme :

class Foo {

  observe() {
    return (
     combineLatest(
      loadUser(this.props.userID),
      loadSomethingElse(this.props.somethingElseId),
      (user, somethingElse) => ({ user, somethingElse})
     )
  }

  render() {
    ..
  }

}

C'est peut-être un peu plus verbeux, mais cela permet d'avoir un beau type statique :

interface Comp<T> {
  observe(): Observable<T>;
  data: T;
}

De plus, au lieu de réexécuter observe lorsque props/state change, nous pouvons avoir accès à 'props' 'state' en tant qu'observable :

class Foo {

  observe(propsStream) {
    return (
      propsStream
        .flatMap(({ userID }) => loadUser(userId))
        .map(user => { user })
    );
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return <Spinner />;
    }
    return <div>Hello, {this.data.user.name} [{this.props.userID}]!</div>;
  }
}

La raison en est que nous ne voulons pas exiger l'utilisation de combinateurs et la compréhension de RxJS pour pouvoir souscrire à (plusieurs) Observables. Combiner deux Observables de cette manière est assez déroutant. En fait, au moins pour nos sources de données, nous implémenterons probablement l'API d'abonnement mais n'inclurons même pas les combinateurs sur le prototype des Observables. Ce n'est pas une exigence, mais vous êtes libre d'utiliser des combinateurs si vous en avez besoin.

Cependant, pour vous abonner à une simple boutique Flux, vous ne devriez pas en avoir besoin.

Je pense que Flow sera probablement capable de gérer ce type statique en utilisant des contraintes, mais je vais vérifier avec ces gars-là pour m'en assurer. Je pense qu'il suffira de taper la propriété data et ensuite le type observer peut être implicite.

class Foo extends React.Component {
  data : { user : User, content : string };
  observe() /* implied as { user : Observable<User>, content : Observable<string> } */ {
  }
}

Ce changement ne consiste pas à se concentrer entièrement sur Observables pour décrire l'état de l'application. Cela peut être mis en œuvre en plus de cela, comme vous l'avez fait auparavant. Il ne s'agit explicitement pas de l'état de l'application puisque cette méthode est idempotente. Le cadre est libre de se désabonner et de se réabonner au besoin.

cc @ericvicenti

Au moins dans le cas du tapuscrit, il n'y aurait aucun moyen de contraindre le type de retour de observe en fonction du type de data , au moins jusqu'à quelque chose comme https://github.com/ Microsoft/TypeScript/issues/1295 est implémenté.

J'adorerais l'utiliser pour la prochaine version de React DnD, mais cela nécessite évidemment d'attendre React 0.14.
Je me demande si je peux "polyfiller" ceci pour le moment avec un composant d'ordre supérieur qui définit this.data sur une instance ref . Peut-être trop fou cependant.

Serait-il possible d'observer les Promesses ? Ensuite, on pourrait utiliser un arbre Promises pour résoudre les données de l'ensemble de l'arbre des composants avant le premier rendu ! Ce serait très utile pour React côté serveur.

Quels sont les avantages d'en faire une API de première classe ? Cela pourrait essentiellement être accompli en utilisant un "composant d'ordre supérieur".

Quels sont les avantages d'en faire une API de première classe ? Cela pourrait essentiellement être accompli en utilisant un "composant d'ordre supérieur".

Envelopper dans 5 HOC pour obtenir 5 abonnements est un peu lourd et plus difficile à comprendre pour les débutants. Comprendre componentWillReceiveProps n'est pas non plus trivial. Cela corrige les deux.

Pour ma part, je souhaite la bienvenue à nos nouveaux seigneurs observables.

Je me demande si cela peut aider à rapprocher https://github.com/chenglou/react-state-stream de l'API vanilla de React

Ne faudrait-il pas qu'un seul HOC ? Dans l'exemple de votre message Medium , vous parcourez stores et vous vous abonnez à chacun.

stores.forEach(store =>
  store.addChangeListener(this.handleStoresChanged)
);

@aaronshaf Dépend du cas d'utilisation, bien sûr. Parfois, il s'agit de différents types de sources d'État, pas seulement de « plusieurs magasins ». Mais je ne peux pas dire au nom de l'équipe React, écoutons ce @sebmarkbage .

J'adorerais une sorte de polyfill pour jouer avec ça maintenant. Je n'ai pas encore complètement saisi l'idée. Quel est le mécanisme impliqué dans le traitement des futures mises à jour. Je vais passer un peu plus de temps à le comprendre. Je pense que cela devrait être faisable avec un simple mixin.

( @vjeux m'a dit que je devrais intervenir ! alors me voilà.)

Je ne veux pas promouvoir mon propre travail, mais je pense que ce crochet est très similaire au crochet getNexusBindings dans React Nexus . Vous déclarez les data deps au niveau du composant via un hook de cycle de vie (qui peut dépendre des props).

L'API ressemble à :

class UserDetails {
  getNexusBindings(props) {
    return {
      // binding to data in the datacenter
      posts: [this.getNexus().remote, `users/${this.props.userId}/posts`],
      // binding to data in the local flux
      mySession: [this.getNexus().local, `session`],
    }
  }
}

La liaison est appliquée/mise à jour pendant componentDidMount et componentWillReceiveProps . Dans ce dernier cas, les liaisons suivantes sont différenciées des liaisons précédentes ; les liaisons supprimées sont désabonnées, les liaisons ajoutées sont souscrites. Le mécanisme de récupération/mise à jour sous-jacent est décrit dans l'implémentation de Nexus Flux . Fondamentalement, avec la même API, vous pouvez soit vous abonner à des données locales (magasins locaux traditionnels) ou à des données distantes (récupérer à l'aide de GET et recevoir des correctifs via Websockets/polyfill). Vous pouvez en fait vous abonner aux données d'une autre fenêtre (en utilisant postWindow) ou d'un WebWorker/ServiceWorker mais je n'ai toujours pas trouvé de cas d'utilisation vraiment utile pour cela.

Pour faire court, vous décrivez de manière synchrone les données au niveau du composant à l'aide d'une abstraction Flux et les crochets garantissent que vos dépendances sont automatiquement souscrites, injectées lors des mises à jour et désabonnées.

Mais il est également livré avec une fonctionnalité intéressante : les mêmes fonctions de cycle de vie sont exploitées pour effectuer la prélecture des données au moment du rendu côté serveur. Fondamentalement, à partir de la racine et de manière récursive à partir de là, React Nexus pré-extrait les liaisons, rend le composant et continue avec les descendants jusqu'à ce que tous les composants soient rendus.

@aaronshaf @gaearon L'avantage d'en faire une première classe est :

1) Il ne ronge pas l'espace de noms des accessoires. Par exemple, le composant d'ordre supérieur n'a pas besoin de réclamer un nom comme data partir de votre objet props qui ne peut être utilisé pour rien d'autre. L'enchaînement de plusieurs composants d'ordre supérieur continue de consommer plus de noms et vous devez maintenant trouver un moyen de garder ces noms uniques. Que se passe-t-il si vous composez quelque chose qui pourrait déjà être composé et que vous avez maintenant un conflit de nom ?

En outre, je pense que la meilleure pratique pour les composants d'ordre supérieur devrait être d'éviter de modifier le contrat du composant enveloppé. C'est-à-dire que conceptuellement, il devrait y avoir les mêmes accessoires dedans que de dehors. Sinon, il est déroutant d'utiliser et de déboguer lorsque le consommateur fournit un ensemble d'accessoires complètement différent de celui reçu.

2) Nous n'avons pas besoin d'utiliser state pour stocker la dernière valeur. Le concept data est similaire à props dans le sens où il s'agit purement d'une mémorisation. Nous sommes libres de le jeter à tout moment si nous avons besoin de récupérer la mémoire. Par exemple, dans un défilement infini, nous pourrions automatiquement nettoyer les sous-arbres invisibles.

@RickWong Oui, il serait assez trivial de prendre en charge Promises puisqu'il s'agit d'un sous-ensemble d'Observables. Nous devrions probablement faire cela pour être sans opinion. Cependant, je déconseillerais probablement de les utiliser. Je trouve qu'ils sont inférieurs aux Observables pour les raisons suivantes :

A) Ils ne peuvent pas être annulés automatiquement par le framework. Le mieux que nous puissions faire est d'ignorer une résolution tardive. En attendant, la promesse conserve des ressources potentiellement coûteuses. Il est facile d'entrer dans une situation difficile d'abonnement/annulation/abonnement/annulation... de longues minuteries/requêtes réseau et si vous utilisez des promesses, elles ne s'annuleront pas à la racine et vous n'aurez donc qu'à attendre le ressources à terminer ou à expiration. Cela peut nuire aux performances des pages de bureau volumineuses (comme facebook.com) ou des applications critiques en termes de latence dans des environnements à mémoire limitée (comme react-native).

B) Vous vous enfermez dans l'obtention d'une seule valeur. Si ces données changent au fil du temps, vous ne pouvez pas invalider vos vues et vous vous retrouvez dans un état incohérent. Il n'est pas réactif. Pour un seul rendu côté serveur qui pourrait convenir, cependant, sur le client, vous devriez idéalement le concevoir de manière à pouvoir diffuser de nouvelles données vers l'interface utilisateur et à les mettre à jour automatiquement pour éviter les données obsolètes.

Par conséquent, je trouve qu'Observable est l'API supérieure à partir de laquelle construire car elle ne vous oblige pas à résoudre ces problèmes si vous en avez besoin.

@elierotenberg Merci d'être intervenu ! Cela semble effectivement très similaire. Même type d'avantages. Voyez-vous des limites à ma proposition ? C'est-à-dire qu'il manque quelque chose, que React Nexus a, que vous ne pourriez pas construire en plus de cela ? Ce serait bien si nous ne nous enfermions pas dans des cas d'utilisation importants. :)

Du point de vue du rendu du serveur, il est important que nous puissions reporter le renderToString final jusqu'à ce que l'Observable/Promise ait été résolu avec des données pouvant être récupérées de manière asynchrone. Sinon, nous sommes toujours dans la position de devoir effectuer toutes les récupérations de données asynchrones en dehors de React sans savoir encore quels composants seront sur la page.

Je pense que react-nexus permet au chargement asynchrone de se produire dans un composant avant de continuer dans l'arborescence de rendu.

Oui, react-nexus sépare explicitement :
1) déclaration de liaison en tant que getNexusBindings (qui est une méthode de cycle de vie synchrone et sans effet secondaire, similaire à render - en fait, elle s'appelait autrefois renderDependencies mais je pensais que c'était déroutant),
2) abonnement/mise à jour de liaison en tant que applyNexusBindings (qui est synchrone et différencie les liaisons de nexus précédentes pour déterminer quelles nouvelles liaisons doivent être souscrites et lesquelles doivent être désabonnées)
3) prélecture de liaison en tant que prefetchNexusBindings (qui est asynchrone et se résout lorsque la valeur "initiale" (quoi que cela signifie) est prête)

ReactNexus.prefetchApp(ReactElement) renvoie un Promise(String html, Object serializableData) . Ce crochet imite la construction de l'arborescence React (en utilisant instantiateReactComponent ) et construit/prefetch/rend les composants de manière récursive. Lorsque toute l'arborescence des composants est 'prête', elle appelle finalement React.renderToString , sachant que toutes les données sont prêtes (erreurs modulo). Une fois résolue, la valeur de cette promesse peut être injectée dans la réponse du serveur. Sur le client, le cycle de vie normal React.render() fonctionne comme d'habitude.

Si quelqu'un veut jouer avec ce type d'API, j'ai créé un polyfill vraiment stupide pour observe tant que composant d'ordre supérieur :

import React, { Component } from 'react';

export default function polyfillObserve(ComposedComponent, observe) {
  const Enhancer = class extends Component {
    constructor(props, context) {
      super(props, context);

      this.subscriptions = {};
      this.state = { data: {} };

      this.resubscribe(props, context);
    }

    componentWillReceiveProps(props, context) {
      this.resubscribe(props, context);
    }

    componentWillUnmount() {
      this.unsubscribe();
    }

    resubscribe(props, context) {
      const newObservables = observe(props, context);
      const newSubscriptions = {};

      for (let key in newObservables) {
        newSubscriptions[key] = newObservables[key].subscribe({
          onNext: (value) => {
            this.state.data[key] = value;
            this.setState({ data: this.state.data });
          },
          onError: () => {},
          onCompleted: () => {}
        });
      }

      this.unsubscribe();
      this.subscriptions = newSubscriptions;
    }

    unsubscribe() {
      for (let key in this.subscriptions) {
        if (this.subscriptions.hasOwnProperty(key)) {
          this.subscriptions[key].dispose();
        }
      }

      this.subscriptions = {};
    }

    render() {
      return <ComposedComponent {...this.props} data={this.state.data} />;
    }
  };

  Enhancer.propTypes = ComposedComponent.propTypes;
  Enhancer.contextTypes = ComposedComponent.contextTypes;

  return Enhancer;
}

Usage:

// can't put this on component but this is good enough for playing
function observe(props, context) {
  return {
    yourStuff: observeYourStuff(props)
  };
}

class YourComponent extends Component {
  render() {
    // Note: this.props.data, not this.data
    return <div>{this.props.data.yourStuff}</div>;
  }
}

export default polyfillObserve(YourComponent, observe);

Observable est-il une chose concrète et convenue en dehors des implémentations de bibliothèques ? Quel est le contrat, est-il assez simple à mettre en œuvre sans avoir besoin d'utiliser bacon ou Rxjs ? Aussi agréable que puisse être une API de première classe pour le chargement latéral des données, il semble étrange que React ajoute une API basée sur une primitive de spécification non spécifiée/très initiale, étant donné le mouvement constant de React vers plain js. Est-ce que quelque chose comme ça nous lierait à une mise en œuvre spécifique d'un terrain utilisateur ?

en aparté pourquoi pas Streams ? Je n'ai pas de cheval dans la course, mais je me demande honnêtement; il y a déjà du travail sur les flux Web, et bien sûr il y a un nœud

@jquense Il y a un travail actif sur une proposition d'ajout d'Observable à ECMAScript 7 (+) donc idéalement cela deviendrait du JS simple. https://github.com/jhusain/asyncgenerator (Actuellement obsolète.)

Nous ne dépendrions pas de RxJS. L'API est simple à implémenter vous-même sans utiliser RxJS. RxJS est le plus proche de la proposition ECMAScript active.

most.js semble faisable aussi.

L'API de Bacon.js semble difficile à consommer sans dépendre de Bacon en raison de l'utilisation des types Bacon.Event pour séparer les valeurs.

Les API Stream sont de trop haut niveau et très éloignées de ce cas d'utilisation.

Existe-t-il une sorte d'option "attendre avant de rendre" ? Je veux dire sur le client, il n'est pas nécessaire d'attendre tous les Observables avant le rendu, mais sur le serveur, vous voudriez attendre qu'ils se résolvent afin que le rendu () de chaque composant soit complet et non partiel.

[parent] await observe(). full render(). -> [foreach child] await observe(). full render().

Dans toutes mes explorations, j'ai découvert qu'il s'agissait du crochet de cycle de vie le plus important manquant dans React côté serveur.

Suite à cette discussion, j'ai essayé de résumer ce que fait React Nexus dans le post suivant :

Applications ismorphiques bien faites avec React Nexus

Voici le schéma de la routine de prélecture principale :

React Nexus

Nous ne dépendrions pas de RxJS. L'API est simple à implémenter vous-même sans utiliser RxJS. RxJS est le plus proche de la proposition ECMAScript active.

:+1: c'est la grande préoccupation pour moi, en pensant, par exemple, aux promesses où la mise en œuvre de la vôtre est extrêmement lourde à moins que vous ne sachiez ce que vous faites. Je pense que sinon vous vous retrouvez avec une exigence implicite sur une bibliothèque spécifique dans l'écosystème. Tangentiellement ... l'une des bonnes choses du monde des promesses est la suite de tests A +, donc même dans toutes les bibliothèques, il y avait au moins une assurance d'une fonctionnalité commune de .then , ce qui était utile pour l'interopérabilité des promesses avant qu'elles ne le soient standardisé.

c'est la grande préoccupation pour moi, en pensant, par exemple, aux promesses où la mise en œuvre de la vôtre est extrêmement difficile à moins que vous ne sachiez ce que vous faites. Je pense que sinon vous vous retrouvez avec une exigence implicite sur une bibliothèque spécifique dans l'écosystème.

Complètement d'accord. Heureusement, les observables ont un contrat très simple et n'ont même pas de méthodes intégrées comme then donc d'une certaine manière, ils sont encore plus simples que les promesses.

Ils pourraient devenir plus compliqués (et plus lents) si le comité insiste pour qu'appeler next programme une micro-tâche comme Promises.

Cela dérangerait beaucoup de modèles basés sur le fait que onNext est synchrone dans RxJS :/

Je pense qu'un modèle de magasin Flux courant pourrait être de conserver une carte des observables par clé afin qu'elles puissent être réutilisées. Ensuite, nettoyez-les lorsque tout le monde se désabonne.

De cette façon, vous pouvez faire des choses comme : MyStore.get(this.props.someID) et toujours récupérer le même Observable.

De cette façon, vous pouvez faire des choses comme : MyStore.get(this.props.someID) et toujours récupérer le même Observable.

Est-ce que l'utilisation de this.props.key (parti, je sais) aurait du sens ? Dans la plupart des cas, vous passez déjà cet identifiant unique avec <... key={child.id} .. /> .

De cette façon, vous pouvez faire des choses comme : MyStore.get(this.props.someID) et toujours récupérer le même Observable.

C'est aussi le modèle que j'utilise pour React Nexus. Store#observe renvoie un observateur mémorisé et immuable ; il est nettoyé (y compris un mécanisme de nettoyage spécifique au backend, tel que l'envoi d'un véritable message de "désabonnement" ou autre) lorsque tous les abonnés sont partis pendant au moins un tick.

@sebmarkbage @gaearon Comment observerait-on le travail sur le serveur dans la v0.14 ?
Serait-il capable d'attendre correctement que tous les observateurs se résolvent avant de rendre une chaîne similaire à la façon dont react-nexus le fait (mais intégré pour réagir)?

IMO, ce serait formidable si les composants attendaient la première valeur observée avant d'être "prêts" pour le rendu sur le serveur.

@gaearon : IMO, ce serait formidable si les composants attendaient la première valeur observée avant d'être "prêts" pour le rendu sur le serveur.

Oui, :+1 : pour le rendu asynchrone. En attendant react-async par @andreypopp est une alternative, mais il faut fibers pour "hacker" React. Ce serait formidable si React pouvait prendre en charge le rendu asynchrone prêt à l'emploi.

Le rendu asynchrone est quelque chose que nous aimerions prendre en charge mais ne fait pas partie de ce problème. L

Probablement pas en 0.14 malheureusement. De nombreuses conceptions différentes à considérer et à refactoriser sont nécessaires.

N'hésitez pas à créer et publier des descriptions des changements architecturaux apportés aux éléments internes nécessaires pour que cela se produise.

J'ai eu la même pensée que @gaearon concernant : react -streaming-state . Compte tenu de toutes les applications potentielles autres que le chargement latéral, pourrait-il y avoir un meilleur nom que data ? Par exemple, observed l'associerait plus clairement à la méthode.

Je ne veux pas dérailler avec le bikeshedding, mais je voulais jeter ça là-bas.

ne peut pas attendre les observables dans React. cela devrait rendre React réactif si je comprends bien

J'expérimente une idée similaire lors de la réécriture react-async , voir README .

La différence notable est que j'introduis une identité observable/processus explicite pour réconcilier les processus, de la même manière que React le fait avec les composants prop et stateful key .

Lorsque id du processus nommé change, React Async arrête l'ancienne instance de processus et en démarre une nouvelle.

L'API ressemble à :

import React from 'react';
import Async from 'react-async';

function defineFetchProcess(url) {
  return {
    id: url,
    start() {
      return fetch(url)
    }
  }
}

function MyComponentProcesses(props) {
  return {
    user: defineFetchProcess(`/api/user?user${props.userID}`)
  }
}

@Async(MyComponentProcesses)
class MyComponent extends React.Component {

  render() {
    let {user} = this.props
    ...
  }
}

L'API de processus suit désormais l'API ES6 Promises en termes de syntaxe et de nom, mais sémantiquement, il n'est pas prévu que process.then(onNext, onError) soit appelé une seule fois par processus en direct. Il est conçu pour s'adapter au cas d'utilisation le plus populaire (?) de récupération de données via des promesses. Mais pour être honnête, je pense maintenant que je dois le changer pour éviter toute confusion.

Je me trompe peut-être, mais je pense que la seule chose qui empêche l'implémentation de l'API proposée (dans ce numéro) dans l'espace utilisateur est l'absence du crochet de cycle de vie qui s'exécute juste avant le rendu comme componentWillUpdate mais avec un nouveau props et state déjà installés sur l'instance.

Une chose qui n'a pas encore été discutée est la gestion du rappel onError . Si un observable produit une erreur, cette information devrait être disponible pour le composant d'une manière ou d'une autre. Parce que React gère l'appel réel subscribe(callbacks) , nous aurions besoin d'une méthode standardisée pour l'injecter dans cet objet de rappel.

Pour offrir le plus de flexibilité aux développeurs d'applications, je vois deux approches. La première consiste à placer les erreurs sur un attribut de niveau supérieur, similaire à this.data . Cela semble incroyablement lourd et consomme encore plus l'espace de noms du composant.
La seconde permettrait aux développeurs de définir leur propre rappel onError tant que fonction de cycle de vie. Si je voulais avoir une gestion des erreurs personnalisée pour mes observables, je pourrais ajouter quelque chose comme

onObserveError(key, error) {
  // do something with the error
  this.state.errors[key] = error;
  this.setState({ errors: this.state.errors });
}

Ceci est similaire à ce que nous avons fait dans la prochaine itération de Parse+React. Nous avons créé notre propre gestion des erreurs pour produire l'API que nous voulions. Les erreurs sont ajoutées à une carte privée { name => error } et les composants ont une méthode publique de niveau supérieur, queryErrors() , qui renvoie un clone de la carte si elle n'est pas vide, et null sinon (en permettant un simple if (this.queryErrors()) .

Bien sûr, définir de nouvelles méthodes réservées est aussi une affaire délicate ; Je ne prétendrai pas que ce n'est pas le cas. Mais nous avons besoin d'un moyen de rendre implicitement ou explicitement les erreurs disponibles pour le composant lors du rendu.

@andrewimm L'idée est d' https://github.com/facebook/react/issues/2928

Cela devrait également gérer les erreurs dans les méthodes de lancement et de récupération avec élégance. Comme si la méthode render() lancée. C'est beaucoup plus de travail et cela prendra du temps à mettre en œuvre correctement, mais l'idée était d'unifier la gestion des erreurs de cette manière.

Je dirais que cela devrait être laissé en dehors de React proprement dit, et 1 ou 2 points d'intégration clés devraient être coordonnés avec des projets comme React-async et React-nexus afin que cela puisse être fait proprement en plus de React proprement dit....

Je suis d'accord, il semble que d'avoir une suggestion de façon de le faire est mieux que
cuire cela dans le cadre lui-même

Le mar. 21 avril 2015, 23h38 Rodolfo Hansen [email protected]
a écrit:

Je dirais que cela devrait être laissé en dehors de la réaction proprement dite, et 1 ou 2 clés
les points d'intégration doivent être coordonnés avec des projets tels que react-async et
react-nexus afin que cela puisse être fait proprement au-dessus de React proprement dit ....


Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/facebook/react/issues/3398#issuecomment -95048028.

Au cours du week-end, j'ai construit une autre implémentation de Flux, appelée Flexy . En cela, consultez le code du magasin. Il expose une méthode .getObservable qui est conforme à l'API observable, même si elle n'a vraiment aucun observable ou autre cadre réactif utilisé.

Donc, je dirais que l'API est assez facile à créer avec des Observables réels.

Cela dit, ne jugez pas le code trop sévèrement, il a été fait en un week-end pour :

  • amusement
  • compréhension
  • en utilisant js-csp
  • en utilisant l'API d'observation

En passant, un système comme celui-ci et Flux rendent en fait le rendu côté serveur moins pénible. En utilisant un système similaire à React-Nexus, nous pouvons initialiser les magasins et les transmettre à une application React. Nous pouvons ensuite surveiller les magasins et le répartiteur et continuer à restituer jusqu'à ce qu'aucune action ne soit déclenchée (toutes les données requises sont déjà dans les magasins).

Je dirais qu'il s'agit du plus petit point d'intégration pour obtenir la nouvelle sémantique des abonnements aux données sans état. Quels autres points d'intégration suggéreriez-vous ? Sans compter le rendu asynchrone qui est un problème beaucoup plus complexe et mérite son propre fil, et ce crochet pourrait être utilisé avec le rendu asynchrone malgré tout.

Tout le reste est déjà possible à mettre en œuvre en plus de React sur une base par composant. Notez que nous n'allons pas faire d'injections globales de plugins car cela interrompt la réutilisation des composants dans l'environnement, de sorte que les frameworks construits sur React doivent être contextuels aux composants individuels.

Quels crochets manquons-nous?

Hey,

Pour être honnête, même si de nouveaux crochets auraient facilité la mise en œuvre, nous pouvons certainement réaliser un chargement latéral des données sans eux, comme nous l'avons démontré avec react-async et react-nexus .

Si quoi que ce soit, exposer et prendre en charge le maintien du cycle de vie des instances de composants React en dehors d'une hiérarchie React montée aiderait. Dans react-nexus j'utilise le instanciateReactComponent interne et j'appelle moi-même componentWillMount , componentWillUnmount , etc., et je considère cette approche fragile (et si instanciateReactComponents s'appuie sur des invariants internes qui changent sur la prochaine version de React ?).

En ce qui concerne l'approche sans état, il me semble que la récupération de données asynchrones _est_ avec état, et donc le stockage de l'état en attente/terminé/échoué dans l'état de certains composants est pertinent. Lorsque nous utilisons react-nexus dans des applications du monde réel, nous avons des composants d'ordre supérieur qui effectuent la récupération de données et injectent leur état de récupération dans leurs composants enfants en tant qu'accessoires. Le composant interne est donc "sans état" (ce qui est souhaitable), et le composant externe est "avec état" (ce qui est également souhaitable, par exemple pour afficher un spinner de chargement ou un espace réservé).

Quels autres points d'intégration suggéreriez-vous ?

il me semble que @ANDREYPOPP a posé la bonne question. n'est-ce pas la seule chose dont nous avons besoin pour implémenter ceci dans userland le crochet du cycle de vie avant le rendu? cela semble être le plus petit changement d'API minimal nécessaire, le reste consiste à définir et à déclencher forceUpdate de manière appropriée lorsque vous modifiez data en fonction du flux d'entrée/émetteur/observable. À moins qu'il ne me manque quelque chose d'autre de spécial (tout à fait possible) ?

Je ne suis pas entraîné dans la discussion plus large ici.
Mais pour répondre à @sebmarkbage , je pense que l'un des crochets les plus importants que je voudrais est quelque chose qui n'est nécessaire que lorsque vous n'utilisez pas de véritables observables.

  • Un crochet qui peut fournir des fonctions qui peuvent traiter les valeurs poussées par l'observable _avant_ qu'elles soient définies sur des données. Avec de vrais observables, ce serait juste un .map

Si la situation était un peu plus ouverte, je pense qu'il devrait y avoir des crochets pour remplacer le comportement spécifique à Observable par des crochets personnalisés. De cette façon, nous pourrions utiliser des émetteurs d'événements ou des canaux CSP à la place.

Il me semble que les derniers commentaires disent que le plus petit point d'extension est en fait des crochets de cycle de vie "connecter" et "déconnecter" - ce qui facilite l'attachement de données asynchrones à un composant et la gestion de ces abonnements ?

Les observables pourraient alors être assez trivialement construits sur cela (à l'intérieur ou à l'extérieur du noyau) - mais exposer ces points du cycle de vie a un attrait plus large ?

Est-ce un résumé raisonnable?

Je ne suis pas non plus encore convaincu que cela doit être résolu dans React lui-même. Je suis beaucoup plus enclin à penser que React devrait fournir tous les crochets nécessaires pour créer cette fonctionnalité au-dessus de React.

Mais pour un instant, disons que nous allons avec observe() . Quelques réflexions :

Nous avons this.props , this.state , this.context et maintenant this.data , tous comme sources potentielles de nouvelles données dans render() . Cela me paraît excessif. L'idée est-elle de séparer l'état de l'application de l'état du composant ? Cela pourrait clarifier et séparer certains problèmes liés à l'état, mais j'ai l'impression que le coût de l'introduction d'une nouvelle entrée peut ne pas l'emporter sur les gains. Si nous voulons que this.state se concentre uniquement sur l'état du composant, pourquoi ne pas laisser les champs dans this.data s'ajouter à this.props ou this.context place ?

Le nom this.data est trop générique. Les accessoires sont des données, l'état est une donnée et toute variable locale est une donnée. Le nom n'ajoute aucune signification et brouille les significations existantes. Je préférerais this.observed loin commentaire de

pourrait-il y avoir un meilleur nom que data ? Par exemple, observed associerait plus clairement à la méthode.

Si nous laissons observe() s'exécuter sur le serveur, nous avons besoin d'une sorte de crochet qui nettoiera les fuites de mémoire que cela pourrait causer, car le démontage ne se produira jamais.

Si nous appelons à nouveau observe() à chaque fois que props ou state changent (et context ?), alors observe doit être optimisé pour la performance et idéalement ne peut pas être rendu cher accidentellement. Cela devient une partie du chemin chaud. J'aime ça de React Nexus :

les liaisons suivantes sont différenciées des liaisons précédentes ; les liaisons supprimées sont désabonnées, les liaisons ajoutées sont souscrites.

J'en suis venu à croire que l'état complique les composants et j'ai essayé de l'utiliser moins dans mes propres composants et de le soulever à la place. C'est pourquoi, en plus des préoccupations soulevées par @fisherwebdev (avec lesquelles je suis d'accord maintenant), je ne suis pas convaincu que laisser observe dépendre de state soit une bonne idée. L'état dépend des accessoires, l'observation dépend de l'état _et_ des accessoires. N'est-ce pas trop compliqué ? Je préfère avoir observe(props) .

Je me rends également compte qu'observer ne devrait pas dépendre de state , principalement parce qu'il n'en a pas besoin. Comme le souligne @gaearon , il est assez facile de hisser l'État à un niveau supérieur, ce qui semble plus propre après avoir séparé les préoccupations. Lorsque observe peut potentiellement dépendre de state , la logique de gestion des mises à jour au sein du composant devient beaucoup plus complexe ; quand cela ne dépend que de props , vous pouvez avoir des gestionnaires sans fourchette dans componentDidMount / componentWillReceiveProps . Moins de code dans le chemin critique se traduit par un cycle de rendu plus simple et réduit également le nombre de mises à jour involontaires qui relancent les abonnements.

+1 pour observer (accessoires)

Moins nous traitons d'état, mieux c'est IMO.

Je suis d'avis qu'en tant que bibliothèque, React devrait essayer d'être aussi flexible que possible. Je suis d'accord que ce n'est pas une bonne idée pour observer de dépendre de l'état. Oui ça peut être compliqué. Oui, la meilleure pratique devrait être de ne pas dépendre de l'état.
Mais c'est un choix que les utilisateurs de React devraient être autorisés à faire.
Je recommanderais de laisser l'API actuelle inchangée (à l'exception de tous les crochets possibles pour ajouter plus de flexibilité, pour la faire fonctionner avec plus que des observables par exemple), et de prouver la documentation qui explique que l'utilisation de l'état dans la méthode observe n'est pas recommandée.

Je crois que tout le monde veut faire ce qu'il faut et que nous voulons être orientés dans la bonne direction. Le fait d'observer l'état de non-acceptation rend cela plus facile pour l'utilisateur que de trouver accidentellement quelque chose comme "ceci est un anti-modèle" dans la documentation.

EDIT : désolé, je viens de réaliser que je cherchais flatMap . Je me suis confondu parce que je pensais que cela aplatirait le tableau de messages, mais il fonctionne à un niveau supérieur (les messages observables).

L'API proposée gérerait-elle le cas où le résultat d'un champ de données dépend du résultat d'un autre ? C'est-à-dire que vous mappez le premier résultat et renvoyez un observable :

observe(props, context) {
  if (!props.params.threadID) {
    return {};
  }

  const observeThread = ThreadStore.observeGetByID(
    {id: props.params.threadID}
  );
  return {
    thread: observeThread,
    messages: observeThread.map(thread => {
      return MessageStore.observeGetByIDs({ids: thread.messageIDs});
    })
  };
}

Je suis assez nouveau dans les observables en général, donc je me trompe peut-être complètement. Dans le pays des promesses, c'est extrêmement simple, puisque le retour d'une promesse à partir de then fera que les then suivants seront basés sur cette promesse.

Je ne comprends pas les commentaires selon lesquels cela ne devrait pas dépendre de this.state . L'état encapsulé rend sûrement React beaucoup plus compliqué, mais c'est aussi tout. S'il n'y avait pas d'état encapsulé, nous n'aurions besoin que d'une bibliothèque de mode immédiat mémorisée. Si vous vous lancez dans les "magasins", alors oui, vous n'avez pas besoin d'état, mais ce n'est pas ce que React vous prescrit de faire.

Nous avons plusieurs modèles qui vous obligent à créer des wrappers supplémentaires, mais pour une utilisation normale, vous ne devriez pas en avoir besoin. Outre les magasins, vos données observées dépendent toujours de l'état, même indirectement. Je ne pense pas que ce soit une mauvaise pratique du tout d'en dépendre dans l'observation. Par exemple

observe() {
  return { items: Items.getPagedItems({ pageIndex: this.state.currentPage }) };
}

Même si observe ne dépendait pas de state il dépendrait toujours de props et context . Même si cela ne dépendait pas de context vous auriez toujours une indirection qui utilise context pour rendre les accessoires d'un composant qui l'utilisent dans observe .

observe devrait être réévalué à chaque fois qu'une passe de rendu tombe, car les accessoires auraient pu changer. Cependant, nous différencierions certainement les observables résultants et ne nous désabonnerions/réabonnerions si le même observable est renvoyé. Cependant, nous ne pouvons pas différencier les propriétés individuelles (autre qu'avec shouldComponentUpdate), donc idéalement, vous devriez implémenter votre propre cache en utilisant un Map comme fonctionnalité de puissance. De cette façon, vous pouvez renvoyer le même observable à plusieurs composants de l'arbre. Par exemple, plusieurs composants chargeant le même utilisateur. Même si vous ne le faites pas, il vous suffit de recréer l'Observable et d'atteindre éventuellement le cache inférieur. C'est ainsi que fonctionne la réconciliation React de toute façon. Ce n'est pas si lent.

Ce crochet observe n'est pas conçu pour rendre React complètement réactif dans le sens où l'état est capturé dans Observables. L'un des principaux objectifs de conception est actuellement d'éviter de piéger l'état dans les fermetures et les combinateurs et d'avoir à la place un bel arbre d'état séparé qui peut être gelé et réactivé, et potentiellement partagé entre les travailleurs.

Ce qui m'amène à mon dernier point...

Nous n'avons certainement pas _besoin_ d'ajouter ceci à la bibliothèque principale. L'interface "publique" d'origine était mountComponent/receiveComponent et vous pouviez construire tout votre système de composants composites dessus. Cependant, peu de gens ont utilisé le fait qu'il est beaucoup plus puissant d'élever la barre d'abstraction puisque nous pouvons maintenant créer d'autres choses qui sont activées par une barre d'abstraction plus élevée. Comme les optimisations à l'échelle des composants.

L'objectif principal de React est de créer un contrat entre les composants afin que différentes abstractions de l'écosystème puissent coexister. Une partie importante de ce rôle consiste à élever le niveau d'abstraction des concepts communs afin que nous puissions activer de nouvelles fonctionnalités inter-composants. Par exemple, sauvegarder tout l'état d'un sous-arbre, puis relancer le sous-arbre. Ou éventuellement en incluant le démontage automatique sur le serveur ou en modifiant les aspects temporels de la réconciliation sur le serveur.

Il est également important de prévoir quelques piles incluses pour rendre tout cela agréable au goût et uniforme.

Il est important de réaliser que la micro-modularisation (comme l'ajout d'un nouveau crochet de cycle de vie) n'est pas strictement une pure victoire par rapport à sa construction dans le framework. Cela signifie également qu'il n'est plus possible de raisonner sur des abstractions à l'échelle du système.

J'aimerais que quelque chose comme ça soit dans les docs comme "philosophie / objectifs de conception / non-objectifs".

L'objectif principal de React est de créer un contrat entre les composants afin que différentes abstractions de l'écosystème puissent coexister. Une partie importante de ce rôle consiste à élever le niveau d'abstraction des concepts communs afin que nous puissions activer de nouvelles fonctionnalités inter-composants.

Aime ça. Je suis d'accord avec @gaearon que ce serait bien si c'était dans une sorte de doc.

Nous n'avons certainement pas besoin de l'ajouter à la bibliothèque principale.... Cependant, peu de gens ont utilisé le fait qu'il est beaucoup plus puissant pour élever la barre d'abstraction puisque nous pouvons maintenant créer d'autres choses qui sont activées par une barre d'abstraction plus élevée. Comme les optimisations à l'échelle des composants.

Je pense que la réticence (pour moi du moins) n'est pas d'ajouter une autre API, mais d'en ajouter une qui dépend d'une construction non linguistique (toujours en cours de définition) pour fonctionner. Cela pourrait parfaitement fonctionner, mais je m'inquiète des problèmes auxquels sont confrontées les bibliothèques de promesses où aucune bibliothèque de promesses (même celles qui se plaignent des spécifications) ne peut se faire confiance, ce qui conduit à un emballage inutile et à un travail défensif pour s'assurer qu'elles se résolvent correctement, ce qui limite les possibilités d'optimisation . Ou pire encore, vous êtes bloqué comme jQuery avec une implémentation cassée qui ne peut jamais changer.

@jquense Je suis entièrement d'accord. Je voulais ajouter ce crochet il y a longtemps. (Expérience originale : https://github.com/reactjs/react-page/commit/082a049d2a13b14199a13394dfb1cb8362c0768a)

L'hésitation il y a deux ans était que c'était toujours trop éloigné de la normalisation. Je voulais un protocole standard avant de l'ajouter au noyau.

Je pense que nous arrivons à un point où de nombreux cadres d'accord sur la nécessité de quelque chose comme les observables et la normalisation atteignent un point où une API agréable a été proposée. Je suis sûr que nous devrons finir par le modifier un peu, mais aussi longtemps que l'architecture de haut niveau fonctionne, il devrait être échangeable et finalement converger.

Je pense que ce qui s'est passé avec des promesses est que l'histoire de l'API et du débogage manquaient gravement dans certaines zones que les observables ne souffrent pas. C'est une histoire plus complète hors de la boîte où les promesses ont dû normaliser une solution incomplète minimale.

La seule différence d'opinion concernant les observables que j'ai observés (je n'ai pas pu résister, désolé) est le potentiel de Zalgo. Si Observables peut ou non pousser la valeur de manière synchrone en réponse à un abonnement. Certaines personnes semblent contre, mais l'utilisation d'Observables par React en dépendra pour autant que je sache. Peux-tu commenter ceci?

En général, je ne trouve pas que Zalgo soit un problème avec Observables car le consommateur est toujours en contrôle et peut opter pour toujours asynchrone avec quelque chose comme observeOn .

Content qu'il y ait enfin un consensus à ce sujet. Personnellement, je préfère les canaux aux observables, mais si les observables doivent être ajoutés au langage, je conviens qu'il n'est plus nécessaire d'attendre.

Cela dit, assurons-nous de garder l'API suffisamment ouverte pour travailler avec des non-Observables conformes à l'API de base.

Je ne trouve pas que Zalgo soit un problème non plus. Cependant, avec Observables, vous pouvez utiliser un planificateur pour garantir l'asynchronisme si vous le souhaitez, le planificateur par défaut est maintenant asynchrone, vous pouvez donc l'utiliser au besoin.

@sebmarkbage Je pense que vous avez répondu à la plupart de mes préoccupations et je vois maintenant l'avantage d'ajouter cela au cadre. Cependant, pouvez-vous commenter this.data -- (1) pouvons-nous/devrions-nous plier ces champs en accessoires/contexte/état ou (2) pouvons-nous les renommer ?

Un peu bikeshed-y mais allez-y quand même ... le problème de Zalgo n'est pas vraiment de savoir si cela compte en termes d'attentes d'API, c'est un problème d'interopérabilité observable et de facilité de mise en œuvre. Ne pas avoir d'accord précoce sur Zalgo qui a mis le monde de la bibliothèque Promise dans la position ennuyeuse de devoir être super défensif lorsqu'il s'agit de promesses d'autres bibliothèques. (mon point ci-dessus répété ci-dessous)

... où aucune bibliothèque de promesses (même celles qui se plaignent des spécifications) ne peut se faire confiance, ce qui conduit à un emballage inutile et à un travail défensif pour s'assurer qu'elles se résolvent correctement

Parce que les premières promesses n'étaient pas toutes conformes à la résolution asynchrone, nous sommes maintenant dans une position où, même si les bibliothèques sont spécifiées, ne peuvent pas supposer que thenables sont dignes de confiance, ce qui tue les optimisations potentielles. Cela me semble particulièrement pertinent ici, où React ne fournira pas d'implémentation Observable à utiliser (qui le voudrait de toute façon ?), Et nous sommes très probablement à des années de pouvoir compter uniquement sur un navigateur fourni Observable, donc bibliothèque facile l'interopérabilité est importante. En plus du point de @gaearon , si React dépend des appels de synchronisation et qu'il est spécifié qu'il doit toujours être asynchrone, cela nous place dans une position semblable à celle de jquery, où nous sommes bloqués avec une implémentation malveillante.

Je suis complètement d'accord. Je voulais ajouter ce crochet il y a longtemps. L'hésitation d'il y a deux ans était qu'on était encore trop loin de la standardisation. Je voulais un protocole standard avant de l'ajouter au noyau.

Je suis heureux qu'il y ait aussi eu de la participation et qu'on y ait pensé, c'est certainement réconfortant. :) et en général, je pense que l'adoption précoce des promesses a valu les inconvénients dont je parle ici, alors ne prenez pas mon inquiétude comme une aversion ou une désapprobation, je suis assez excité à l'idée d'une API de première classe pour cela et je vois aussi comment Observable est vraiment un choix bon/le plus raisonnable ici pour cela.

"Nous devrions utiliser le contrat RxJS d'Observable car il est plus courant et permet une exécution synchrone, mais une fois que la proposition de @jhusain sera plus courante, nous passerons à ce contrat à la place."

Juste pour ajouter un peu plus de contexte. Il existe une initiative Reactive Streams (http://www.reactive-streams.org/) pour fournir une norme pour le traitement de flux asynchrone avec une contre-pression non bloquante. Cela englobe les efforts visant les environnements d'exécution (JVM et JavaScript) ainsi que les protocoles réseau.

Les principales implémentations actuelles sont par exemple Akka Streams ou RxJava. Je ne sais pas si les RxJ sont déjà conformes à la même interface maintenant, l'interface actuelle pour l'abonnéest onSubscribe(Subscription s), onNext(T t), onCompleted(), onError(Throwable t).

Pouvez-vous nous éclairer davantage sur la proposition de @jhusain ?

Je ne sais pas si React doit strictement se conformer à cette initiative car si j'en ai besoin, je peux probablement mettre RxJs (en supposant qu'il se conformera) entre les deux et s'adapter à l'interface React et laisser des concepts plus avancés comme la contre-pression sur RxJs (bien que je le ferais préférez ne pas trop vous adapter).

Y a-t-il une position ou un objectif concernant cette initiative ?

@vladap Je crois que c'est la proposition susmentionnée de @jhusain

J'ai lu @jhusain et je ne suis pas sûr de la motivation pour éventuellement passer à cette spécification à l'avenir. Y a-t-il un avantage spécifique ?

La spécification Reactive-streams a un support plus large et est déjà sur la version 1.0 . Parce que RxJava implémente déjà cette spécification, je suppose que RxJs suivra (mais n'a pas vérifié).

Ce blog résume les interfaces avec quelques exemples utilisant les flux Akka.

Je vois un avantage à avoir les mêmes interfaces sur le backend et le frontend, principalement parce que je travaille sur les deux. Peut-être que cela pourrait aider à coopérer entre les groupes backend et frontend, mais d'un autre côté, je suppose que websocket ou sse sont les points d'intégration réels pour le streaming.

Je ne trouve pas la liste des implémenteurs sur www.reactive-streams.org pour le moment, mais la dernière fois que j'ai vérifié, c'était :

Björn Antonsson – Typesafe Inc.
Gavin Bierman – Oracle Inc.
Jon Brisbin – Pivotal Software Inc.
George Campbell – Netflix, Inc.
Ben Christensen – Netflix, Inc.
Mathias Doenitz – spray.io
Marius Eriksen – Twitter Inc.
Tim Fox – Red Hat Inc.
Viktor Klang – Typesafe Inc.
Dr Roland Kuhn – Typesafe Inc.
Doug Lea – SUNY Oswego
Stéphane Maldini – Pivotal Software Inc.
Norman Maurer – Red Hat Inc.
Erik Meijer – Applied Dualité Inc.
Todd Montgomery – Kaazing Corp.
Patrik Nordwall – Typesafe Inc.
Johannes Rudolph – spray.io
Endre Varga – Typesafe Inc.

Je vais peut-être trop loin ici, mais je crois que le contexte plus large peut aider dans les décisions futures.

@vladap D'après ce que je comprends et ce que je vois sur les problèmes de github, @jhusain travaille déjà avec eux, donc je suppose que nous n'aurons pas autant de problèmes.
Du point de vue de l'interface, d'après ce que j'ai pu saisir dans différents problèmes de github et autres documents de spécifications, observer respectera certainement l'interface du générateur, donc quelque chose comme :

{
  next(value),
  throw(e),
  return(v)
}

La simple mise en œuvre d'un observable très basique avec une seule méthode 'subscribe' qui respecte cette interface doit être sécurisée pour réagir.

Cela ressemble à un nom différent avec la même fonctionnalité, en ce sens, c'est bien. Je préférerais probablement la même dénomination que dans la spécification, mais au final, je m'en fous tant que ces méthodes font la même chose.

Je ne peux pas évaluer l'équivalent manquant de onSubscribe(). Dans le blog que j'ai mentionné, l'auteur dit que c'est une clé pour contrôler la contre-pression. Je ne sais pas qu'il a d'autres cas d'utilisation. À partir de là, je suppose que React ne se soucie pas de contrôler la contre-pression ou qu'il existe une autre stratégie pour cela. C'est une chose complexe donc je comprends que ce n'est pas une préoccupation de React.

Dois-je comprendre correctement que la stratégie est quelque chose dans le sens - soit l'application est complexe et il peut survenir des problèmes de contre-pression, puis utilisez quelque chose entre les deux pour le résoudre, comme RxJS, ou vous connectez le composant React directement à fe websocket et vous ne Je n'ai pas de problèmes de contre-pression car l'application est simple et les mises à jour sont lentes.

Où puis-je trouver des propositions d'interfaces observables pour le futur ECMAScript ? S'il y en a déjà.

La proposition actuelle est disponible ici :

https://github.com/jhusain/asyncgenerator

JH

Le 7 mai 2015, à 02h32, vladap [email protected] a écrit :

Où puis-je trouver des propositions d'interfaces observables pour le futur ECMAScript ? S'il y en a déjà.


Répondez directement à cet e-mail ou consultez-le sur GitHub.

La proposition de flux réactifs (RSP) va plus loin que la proposition TC-39 car elle introduit un observable qui gère la contre-pression. Le RSP Observable est optimisé pour envoyer efficacement des flux sur le réseau, tout en respectant la contre-pression. Il est basé en partie sur le travail effectué dans RxJava, qui est une pièce d'ingénierie très impressionnante (divulgation complète : il a été conçu par un collègue de Netflix, Ben Christensen).

La principale raison de la décision de standardiser le type Observable plus primitif est la prudence. L'Observable plus primitif est le dual du contrat ES2015 Iterable, qui nous apporte de précieuses garanties que le type est au moins aussi flexible qu'un type déjà standardisé dans ES2015. De plus, il existe une grande variété de cas d'utilisation dans JS pour Observables qui ne nécessitent pas de contre-pression. Dans le navigateur, le DOM est le récepteur le plus courant pour les flux push et agit efficacement comme un tampon. Étant donné que le type RSP est plus complexe, notre approche consiste à normaliser d'abord le type le plus primitif, puis à laisser de la place pour implémenter le type le plus avancé plus tard. L'idéal serait d'attendre qu'il soit validé en user-land.

FYI RxJS n'a actuellement aucun plan pour mettre en œuvre RSP Observable.

JH

Le 7 mai 2015, à 2h30, vladap [email protected] a écrit :

Cela ressemble à un nom différent avec la même fonctionnalité, en ce sens, c'est bien. Je préférerais probablement la même dénomination que dans la spécification, mais au final, je m'en fous tant que ces méthodes font la même chose.

Je ne peux pas évaluer l'équivalent manquant de onSubscribe(). Dans le blog que j'ai mentionné, l'auteur dit que c'est une clé pour contrôler la contre-pression. Je ne sais pas qu'il a d'autres cas d'utilisation. À partir de là, je suppose que React ne se soucie pas de contrôler la contre-pression ou qu'il existe une autre stratégie pour cela. C'est une chose complexe donc je comprends que ce n'est pas une préoccupation de React.

Dois-je comprendre correctement que la stratégie est quelque chose dans le sens - soit l'application est complexe et il peut survenir des problèmes de contre-pression, puis utilisez quelque chose entre les deux pour le résoudre, comme RxJS, ou vous connectez le composant React directement à fe websocket et vous ne Je n'ai pas de problèmes de contre-pression car l'application est simple et les mises à jour sont lentes.


Répondez directement à cet e-mail ou consultez-le sur GitHub.

Un grand merci pour les précieux détails. Cela a beaucoup de sens.

@gaearon je t'ai en quelque sorte copié. Je voulais utiliser ParseReact avec les classes ES6, j'avais donc besoin de réimplémenter l'API d'observation du mixin en tant que composant d'ordre supérieur.

https://gist.github.com/amccloud/d60aa92797b932f72649 (utilisation ci-dessous)

  • Je permets à observer d'être défini sur le composant ou passé dans le décorateur. (je pourrais fusionner les deux)
  • Je fusionne dans les observables en tant qu'accessoires (pas de this.data ou this.props.data)

@aaronshaf @gaearon L'avantage d'en faire une première classe est :

1) Il ne ronge pas l'espace de noms des accessoires. Par exemple, le composant d'ordre supérieur n'a pas besoin de revendiquer un nom comme les données de votre objet props qui ne peuvent être utilisées pour rien d'autre. L'enchaînement de plusieurs composants d'ordre supérieur continue de consommer plus de noms et vous devez maintenant trouver un moyen de garder ces noms uniques. Que se passe-t-il si vous composez quelque chose qui pourrait déjà être composé et que vous avez maintenant un conflit de nom ?

En outre, je pense que la meilleure pratique pour les composants d'ordre supérieur devrait être d'éviter de modifier le contrat du composant enveloppé. C'est-à-dire que conceptuellement, il devrait y avoir les mêmes accessoires dedans que de dehors. Sinon, il est déroutant d'utiliser et de déboguer lorsque le consommateur fournit un ensemble d'accessoires complètement différent de celui reçu.

Au lieu d'un HOC, il peut s'agir d'un composant standard :

import Observe from 'react/addons/Observe';

class Foo {
  render() {
    return (
      <Observe
        render={this.renderData}
        resources={{
          myContent: xhr(this.props.url)
        }} />
    );
  }

  renderData({ myContent }) {
    if (myContent === null) return <div>Loading...</div>;
    return <div>{myContent}</div>;
  }
}

Parce que vous contrôlez la fonction passée en tant que prop render , il n'y a aucun moyen pour que les noms entrent en collision. En revanche, il ne pollue pas l'état du propriétaire.

Qu'est-ce que j'oublie ici?

Cela pourrait même être moins verbeux si le composant Observe vient de saisir Observables à partir de ses props :

render() {
  return (
    <Observe myContent={Observable.fetch(this.props.url)}
             render={this.renderData} />
  );
}

renderData({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

Ce qui est également bien à ce sujet, c'est que cela évitera les réabonnements si shouldComponentUpdate retourne faux parce que nous n'entrerons pas du tout dans render .

Enfin, on pourrait écrire un décorateur pour render qui l'enveloppe dans un composant Observe :

@observe(function (props, state, context) {
  myContent: Observable.fetch(props.url)
})
render({ myContent }) {
  if (myContent === null) return <div>Loading...</div>;
  return <div>{myContent}</div>;
}

Je préférerais conserver render en tant que fonction de rendu pure au lieu d'y injecter une logique de récupération de données.
La proposition initiale me semble bonne. C'est très proche du fonctionnement de l'état avec rx-react et cela permettra de séparer la gestion de l'état de la logique de récupération des données qui semble très cohérente.

La seule chose qui me dérange est l'utilisation de la carte des observables au lieu d'un observable, car cela ne donne pas à l'utilisateur la possibilité de choisir la composition des observables, mais c'est un problème mineur.

Il ne s'agit pas vraiment d'injecter la logique de récupération des données, mais d'économiser un peu de frappe. Il sera désucré à la version ci-dessus, qui rend juste le composant <Observe /> . Il n'est pas inhabituel de rendre des composants avec état, donc je ne pense pas que cela rende render moins pur qu'il ne l'est maintenant.

J'ai essayé de combiner le meilleur de #3858 et cette proposition.

Dans toutes les approches HOC, l'avantage est l'explicitation, mais les inconvénients sont décrits par @sebmarkbage : les noms des accessoires peuvent entrer en conflit à un moment donné.

Dans la proposition actuelle, l'avantage est l'explicitation, mais le côté négatif est le cycle de vie plus compliqué et la plus grande surface de l'API du composant central.

Dans # 3858, l'avantage est de colocaliser les dépendances de "rendu mémorisé" avec le rendu lui-même (elles ne sont utilisées nulle part ailleurs, donc c'est logique), mais je suis préoccupé par le "semble synchronisé mais est asynchrone" , et je ne comprends pas comment il peut fonctionner avec des modèles immuables s'il s'appuie autant sur this . Cela me déplaît également de la manière React-était-facile à raisonner, car il n'y a rien de facile à raisonner sur le suivi manuel des modifications et le couplage des sources de données à React (ou leur emballage pour travailler avec React). Je comprends qu'il existe une pression pour mettre en œuvre quelque chose à la fois performant et réduisant le passe-partout.

Dans ma proposition, je garde la colocation et l'explicite, mais :

  • <Observe /> (ou le décorateur observe() qui enveloppe le rendu avec <Observe /> ) n'est qu'un module complémentaire, je ne propose aucun changement au noyau de React.
  • Chacun peut implémenter sa propre logique d'observation pour ses cas d'utilisation. Vous pouvez avoir votre propre <Observe /> qui n'utilise qu'un seul observable, si c'est ce que vous préférez. Vous pouvez ou non utiliser le décorateur.
  • Le cycle de vie des composants reste le même.
  • Il n'y a pas de conflits d'accessoires car les données sont transmises en tant que paramètre.
  • Nous faisons du dogfooding en résolvant les problèmes avec les outils dont nous disposons.
  • Pour réduire le passe-partout, nous utilisons des outils de réduction passe-partout (décorateurs) au lieu d'introduire de nouveaux concepts de base.

Grande discussion et travail tout autour ici, beaucoup de respect. :)

Je suis d'accord que cela ne mérite pas d'en faire un noyau. Peut-être qu'un module complémentaire donne à cette proposition suffisamment de traction pour que les gens puissent essayer de converger et de la normaliser avant de s'engager plus pleinement. Cela étant dit, je trouve cette proposition meilleure que https://github.com/facebook/react/issues/3858 et https://github.com/facebook/react/pull/3920 pour son minimalisme.

C'est quelque chose que j'ai utilisé sur des projets parallèles (donc des grains de sel) - c'est similaire au travail impressionnant de @elierotenberg mais ne prend pas en charge le cycle de vie, car cette application n'est pas à 100% dans React et doit interopérer.

Préparez-vous pour CoffeeScript et mixins, ou continuez à plisser les yeux jusqu'à ce que cela ressemble à ES6 si vous préférez. :)

_ = require 'lodash'

module.exports = DeclareNeedsMixin = 
  componentDidMount: ->
    <strong i="12">@needsConsumerId</strong> = _.uniqueId @constructor.displayName
    <strong i="13">@sinkNeeds</strong> <strong i="14">@props</strong>, <strong i="15">@state</strong>

  componentWillUpdate: (nextProps, nextState) ->
    <strong i="16">@sinkNeeds</strong> nextProps, nextState

  componentWillUnmount: ->
    @props.flux.declareNeeds <strong i="17">@needsConsumerId</strong>, []

  sinkNeeds: (props, state) ->
    if not @declareNeeds?
      return console.warn 'Missing method required for DeclareNeedsMixin: `declareNeeds`', @

    needs = <strong i="18">@declareNeeds</strong> props, state
    props.flux.declareNeeds <strong i="19">@needsConsumerId</strong>, needs

  # Intended to be overridden by the host class.
  # Returns a set of facts, stored as an array.
  # Yes, immutable data is awesome, that's not the point here though. :)
  # Facts are serializable data, just values.
  # declareNeeds: (props, state) ->
  #   []

Et utilisé comme ceci :

module.exports = EmailThreads = React.createClass
  displayName: 'EmailThreads'
  mixins: [DeclareNeedsMixin]

  propTypes:
    flux: PropTypes.flux.isRequired

  declareNeeds: (props, state) ->
    [Needs.GmailData.myThreads({ messages: 20 })]

  ...

Ainsi, declareNeeds est une fonction à sens unique des accessoires et de l'état vers une description de ce dont ce composant a besoin. Dans l'implémentation réelle ici, l'extrémité réceptrice de @props.flux.declareNeeds , qui est configurée au niveau d'un composant de niveau supérieur, absorbe ces besoins dans un objet ProcessSink . Il regroupe comme il le souhaite, dédouble needs entre les composants qui partagent le même flux , puis effectue des effets secondaires pour répondre à ces besoins (comme se connecter à un socket ou faire des requêtes HTTP). Il utilise le comptage de références pour nettoyer les éléments avec état tels que les connexions de socket lorsqu'il n'y a plus de composants qui en ont besoin.

Les données circulent à partir de bits avec état tels que les événements de socket et les demandes dans le répartiteur (puis vers les magasins ou ailleurs) et vers les composants pour répondre à leurs besoins. Ce n'est pas synchrone et donc tous les composants gèrent le rendu lorsque les données ne sont pas encore disponibles.

Je partage cela ici uniquement en tant qu'autre voix que l'exploration de ces types de solutions est possible dans l'espace utilisateur, et que l'API actuelle sert très bien ce type d'expérimentation. En termes d'étape minimale que le noyau pourrait prendre pour prendre en charge l'interopérabilité entre différentes approches, je pense que @elierotenberg a réussi :

Si quoi que ce soit, exposer et prendre en charge le maintien du cycle de vie des instances de composants React en dehors d'une hiérarchie React montée aiderait.

En ce qui concerne l'approche sans état, il me semble que la récupération de données asynchrones est avec état, et donc le stockage de l'état en attente/terminé/échoué dans l'état de certains composants est pertinent.

@elierotenberg et @andrewimm font un excellent point sur la gestion des erreurs de première classe. @sebmarkbage Je pense que votre instinct vers le point d'interopérabilité minimal est exact, mais je ne sais pas comment l'ajout de sémantique pour les erreurs pour remonter l'arborescence des composants répond à cette exigence. Il doit y avoir une histoire tout aussi simple ici pour savoir comment accéder aux valeurs de onError et onCompleted , même si c'est juste que les emplacements dans this.observed contiennent la dernière valeur de next/error/completed comme { next: "foo" } . Sans soutenir le contrat observable en tant que fonctionnalité de première classe, je suis un peu sceptique quant à cette proposition qui fait la coupe.

Et puisqu'il s'agit d'Internet, et que c'est l'une de mes premières fois à intervenir ici : le fil d'actualités React est l'une des meilleures lectures et une source complète de travail et d'idées formidables. :+1:

ça sent la reliure pour moi.

Je ne sais pas comment cela se rapporte à la proposition / mise en œuvre actuelle, mais j'ai constaté que la composition et la manipulation d'ordre supérieur sont en réalité une caractéristique essentielle du suivi de la dépendance des données, en particulier si vous utilisez une source de données réactive (flux, flux sur le fil, ou toute autre chose que vous fournit des mises à jour).

Prenons l'exemple suivant avec react-nexus@^3.4.0 :

// the result from this query...
@component({
  users: ['remote://users', {}]
})
// is injected here...
@component(({ users }) =>
  users.mapEntries(([userId, user]) =>
    [`user:${userId}`, [`remote://users/${userId}/profile`, {}]]
  ).toObject()
))
class Users extends React.Component {
  // ... this component will receive all the users,
  // and their updates.
}

Dans l'ensemble, je me demande s'il devrait y avoir une liaison de données dans l'API du composant. Il me semble que les décorateurs de retour d'ordre supérieur - Les décorateurs de retour d'ordre offrent une très belle façon d'exprimer la liaison de données sans polluer l'espace de noms de méthodes de composant.

Cependant, comme l'a noté @sebmarkbage , il existe un risque de pollution de l'espace de noms des accessoires à la place. Pour l'instant, j'utilise un décorateur de transformateur accessoires ( react-transform-props ) pour nettoyer / renommer les accessoires avant de les transmettre à la composante intérieure, mais je reconnais que cela pourrait devenir problématique à l'avenir si les composants d'ordre supérieur deviennent plus banale et les risques de collision de noms augmentent.
Cela pourrait-il être résolu en utilisant des symboles sont des clés de propriété? Est-ce propTypes la vérification Symbol prend en charge les props à clé Symbol ? JSX prendra-t-il en charge les clés d'accessoires en ligne calculées (bien que l'on puisse toujours utiliser les propriétés calculées + la propagation d'objets) ?

Désolé si cela devient un peu hors sujet, mais il me semble que nous devons encore trouver la bonne abstraction/API pour exprimer les données au niveau du composant.

mes 2 ¢

Je me suis bien amusé avec les «enfants en tant que fonction» des valeurs qui changent avec le temps. Je crois que @elierotenberg est arrivé en premier? Mon utilisation actuelle consiste à modéliser des ressorts (via le rebond) sur les ressorts de réaction. Exemple -

<Springs to={{x: 20, y: 30}} tension={30}>
  {val => <div style={{left: val.x, top: val.y}}>moving pictures</div>}
</Springs>

Pas de collision des accessoires et sans usage de l'état du propriétaire. Je peux aussi nier plusieurs sources et réagir gère tous les bits durs. Ces "observables" (ha!) Pourraient également accepter onError onComplete et autres accessoires (graphql requis?).

Une tentative approximative de l'esquisser pour «flux»

<Store 
  initial={0}
  reduce={(state, action) => action.type === 'click'? state+1 : state} 
  action={{/* assume this comes as a prop from a 'Dispatcher' up somewhere */}}> 
    {state => <div onClick={() => dispatch({type: 'click'})}> clicked {state} times</div>}
</Store>

Nous avons utilisé ce modèle, ce que nous avons appelé _render callbacks_, chez Asana, mais nous nous en éloignons finalement.

Avantages

  • Aucun risque de collision d'accessoires.
  • Facile à mettre en œuvre à la fois en tant qu'auteur de StoreComponent et en tant qu'utilisateur de StoreComponent

Les inconvénients

  • shouldComponentUpdate est extrêmement difficile à mettre en œuvre car le rappel de rendu peut se fermer sur l'état. Imaginez que nous ayons un arbre de composants de A -> Store -> B . A a un compteur dans son état auquel on accède pendant le rappel de rendu en tant qu'accessoires pour B . Si A jour à cause du compteur et que Store ne se met pas à jour, B aura une version obsolète du compteur. En conséquence, nous avons été obligés de toujours mettre à jour le Store.
  • Tester des composants isolément est devenu très difficile. Inévitablement, les développeurs ont mis une logique complexe dans les rappels de rendu pour quel composant rendre et ont voulu tester la logique. La façon naturelle de le faire était de rendre l'arborescence entière et de tester _via_ le composant store. Cela rendait impossible l'utilisation du moteur de rendu superficiel.

Nous passons maintenant à un modèle où le StoreComponent prend un ReactElement et lorsque le magasin reçoit de nouvelles données, le magasin clone le ReactElement en remplaçant un accessoire spécifique. Il existe de nombreuses façons de créer ce modèle, comme prendre un constructeur de composants et des accessoires. Nous avons opté pour cette approche car c'était la plus facile à modéliser dans TypeScript.

Des points formidables, je ne peux pas non plus penser à un moyen de contourner l'exigence «de côté».

@threepointone Cela ressemble exactement à l'une des propositions de mise en œuvre pour alimenter https://github.com/reactjs/react-future/pull/28

@ pspeter3 dans votre exemple, y a-t-il un sérieux inconvénient à ce que Store soit toujours mis à jour / shouldComponentUpdate: ()=> true ? Je pense que ses "enfants" auraient été rendus sans Store dans la chaîne de toute façon. Merci pour votre temps!

@threepointone C'est exactement ce que nous avons fait pour nos frontières. On ne savait pas exactement quel était l'impact, mais il y avait des soucis de performance de la part des membres de l'équipe. Les soucis combinés aux tests de difficulté ont obligé à passer à l'utilisation de React.cloneElement(this.props.child, {data: this.state.data})

@ pspeter3 l'angle de test est définitivement un problème. Et si le moteur de rendu peu profond reconnaissait les "rappels de rendu" ? Cela aiderait-il ?

Ps- 'Render callback' :thumbs_up:

@sebmarkbage la discussion actuelle sur es-observable est que subscribe serait garanti asynchrone, avec une méthode [Symbol.observer] fournie pour raccourcir et s'abonner de manière synchrone, bien que la validité d'avoir les deux apis sync/async soit actuellement en débat.

J'avais sonné sur ce ticket avec le cas d'utilisation mentionné ci-dessus en faveur de l'abonnement synchrone, je ne savais pas si vous pourriez avoir quelque chose à ajouter là-bas.

Je pense que les idées ici sont un modèle très propre pour gérer l'état externe, bien qu'après avoir joué un peu avec, je penche en faveur de l'approche HOC.

Aussi @gaearon j'ai simplifié votre bit HOC ci-dessus, en utilisant un statique - ComposedComponent.observe et en utilisant this.state plutôt que this.state.data - https://gist.github.com/tgriesser/d5d80ade6f895c28e659

Ça a l'air vraiment sympa avec les décorateurs es7 proposés :

<strong i="20">@observing</strong>
class Foo extends Component {
  static observe(props, context) {
    return {
      myContent: xhr(props.url)
    };
  }
  render() {
    var myContent = this.props.data.myContent;
    return <div>{myContent}</div>;
  }
}

Le décorateur de classe pourrait même aller jusqu'à ajouter un getter pour data pour le rapprocher de l'API proposée à l'origine (moins l'état local affectant les abonnements observables, ce qui, je suis d'accord, est une bonne chose - beaucoup moins de bruit autour S'inscrire Se désinscrire).

le manque d'abonnement synchrone pourrait être un énorme problème.

Je pense que je sais comment résoudre "le problème d'état" et "le chargement latéral des données" (données dérivées) de manière cohérente. Il le fait dans un "React-way" sans état. J'ai trouvé un moyen de maintenir la cohérence de l'état à tout moment et cela correspond au modèle UI = React(state) . Il est peu probable que je sois hors de contrôle pour le rendre complètement à l'épreuve des balles, ajouter plus d'exemples et faire une bonne présentation. https://github.com/AlexeyFrolov/slt . D'autre part, il est bien testé et je l'utilise de manière itérative dans mes projets de production. Les esprits intelligents sont les bienvenus pour y contribuer.

Salut tout le monde,

C'est drôle que je ne sois pas tombé sur ce fil auparavant, car dans notre entreprise, nous rencontrions les mêmes problèmes que ceux abordés par cette proposition il y a six mois.
Nous avons commencé à utiliser React pour un projet à grande échelle (pensez à un éditeur avec la complexité de Microsoft Visio, avec des données très cycliques). La vanille ne compte pas pour répondre à nos exigences de performance,
et flux était un peu interdit pour nous en raison de sa grande quantité de passe-partout et de la propension aux erreurs de tous les abonnements. Nous avons donc compris que nous avions également besoin de structures de données observables.

Comme nous ne trouvions rien de disponible prêt à l'emploi, nous avons construit notre propre bibliothèque d'observables, basée sur les principes des observables knock-out (notamment : abonnements automatiques).
Cela s'intègre très bien dans le cycle de vie actuel des composants React, et nous n'avons pas eu besoin de hacks bizarres ou même de rappels de rendu enfant (l'ObserverMixin utilisé ci-dessous est d'environ 10 loc).
Cela a beaucoup amélioré notre DX et a si bien fonctionné pour notre équipe que nous avons décidé de le publier en open source . En attendant, il est assez éprouvé au combat (il fournit un polyfill de tableau observable ES7 par exemple) et hautement optimisé.
Voici un exemple de minuterie courte (également disponible en tant que JSFiddle ), à mon humble avis, le DX ne pourrait pas être beaucoup mieux... :relieved:

var store = {};
// add observable properties to the store
mobservable.props(store, {
    timer: 0
});

// of course, this could be put flux-style in dispatchable actions, but this is just to demo Model -> View
function resetTimer() {
    store.timer = 0;
}

setInterval(function() {
    store.timer += 1;
}, 1000);

var TimerView = React.createClass({
    // This component is actually an observer of all store properties that are accessed during the last rendering
    // so there is no need to declare any data use, nor is there (seemingly) any state in this component
    // the combination of mobservable.props and ObserverMixin does all the magic for us.
    // UI updates are nowhere forced, but all views (un)subscribe to their data automatically
    mixins: [mobservable.ObserverMixin],

    render: function() {
        return (<span>Seconds passed: {this.props.store.timer}</span>);
    }
});

var TimerApp = React.createClass({
    render: function() {
        var now = new Date(); // just to demonstrate that TimerView updates independently of TimerApp
        return (<div>
            <div>Started rendering at: {now.toString()}</div>
            <TimerView {...this.props} />
            <br/><button onClick={resetTimer}>Reset timer</button>
        </div>);
    }
});

// pass in the store to the component tree (you could also access it directly through global vars, whatever suits your style)
React.render(<TimerApp store={store} />, document.body);

Pour plus de détails sur cette approche, consultez ce blog . BTW, je vais m'assurer qu'un décorateur et/ou un conteneur sera ajouté à la lib , pour ceux qui utilisent les classes ES6.

Malheureusement, je n'avais pas vu ce fil avant react-europe, sinon nous aurions eu une belle occasion de discuter de React & observables. Mais un grand merci pour les discussions inspirantes ! :+1: J'ai particulièrement aimé les abstractions de GraphQL et le travail de réflexion derrière Redux :)

@mweststrate Je pense que la communauté finira par devoir choisir entre les solutions "Observables" et "Données immuables". Peut-être devons-nous mélanger d'une manière ou d'une autre pour avoir les points forts des deux approches dans une seule solution (https://github.com/AlexeyFrolov/slt/issues/4). Dans ma solution, j'ai implémenté l'approche "Données immuables" en mettant l'accent sur la cohérence de l'état à tout moment. Je prévois également de prendre en charge les observables et les générateurs. Il s'agit d'un exemple de règles utilisées pour récupérer des données "dérivées" ou "complémentaires" (telles qu'un corps de page, des ressources, des recommandations, des commentaires, des mentions J'aime) et maintenir la cohérence de l'état de l'application.

https://github.com/AlexeyFrolov/slt#rules -example

Il s'agit d'un exemple complexe du monde réel (mon code de production) qui montre comment gérer les redirections d'API avec un en-tête Location.

import r from "superagent-bluebird-promise";
import router from "./router";

export default {
    "request": function (req)  {
        let route = router.match(req.url);
        let session = req.session;
        route.url = req.url;
        return this
            .set("route", route)
            .set("session", req.session);
    },
    "route": {
        deps: ["request"],
        set: function (route, request) {
            let {name, params: { id }} = route;
            if (name === "login") {
                return this;
            }
            let url = router.url({name, params: {id}});
            let method = request.method ? request.method.toLowerCase() : "get";
            let req = r[method]("http://example.com/api/" + url);
            if (~["post", "put"].indexOf(method)) {
                req.send(request.body);
            }
            return req.then((resp) => {
                let ctx = this.ctx;
                let path = url.substr(1).replace("/", ".");
                if (!resp.body) {
                    let location = resp.headers.location;
                    if (location) {
                        ctx.set("request", {
                            method: "GET",
                            url: location.replace('/api', '')
                        });
                    }
                } else {
                    ctx.set(path, resp.body);
                }
                return ctx.commit();
            });
        }
    }
}

Dans le reste, c'est la même approche que la vôtre, sauf qu'il n'y a pas de liaison directe avec React (ce n'est pas nécessaire dans mon cas). Je crois que nous devons unir nos forces d'une manière ou d'une autre pour archiver l'objectif commun.

Je pense que c'est une mauvaise idée car il y aura de nombreux cas extrêmes difficiles à considérer.

À partir des commentaires ci-dessus, je peux répertorier les étendues possibles auxquelles l'utilisateur final doit penser chaque fois qu'il souhaite créer un composant "dataful":

  • Rendu côté serveur
  • Initialisation de .state et .data
  • .context , .props , .state et .observe
  • Rendu asynchrone

Je pense que cette proposition conduira à des composants sujets aux erreurs, instables et difficiles à déboguer.

Je suis d'accord avec les hooks de cycle de vie proposés par @glenjamin connect et disconnect .

Toutes mes excuses si c'est hors sujet (je ne sais pas trop). Mais voici un exemple de la raison pour laquelle je pense que l'exposition de l'API pour interagir avec l'arborescence des composants serait une manière intéressante d'aborder ce problème : https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit -todomvc/loggit/renderers/precompute_react_renderer.js#L72

Cette méthode est mon piratage pour parcourir l'arbre, et donc ma question est de savoir comment le fait d'avoir une API publique pour faire ce genre de chose permettrait l'innovation ici dans le "chargement de données latéral", ce qui, je pense, se résume à "un moteur de rendu qui ne t fonctionne de haut en bas": https://github.com/kevinrobinson/redux/blob/feature/loggit-todomvc/examples/loggit-todomvc/loggit/react_interpreter.js#L8

Mais voici un exemple de la raison pour laquelle je pense que l'exposition de l'API pour interagir avec l'arborescence des composants serait une manière intéressante d'aborder ce problème.

Je crois que @swannodette en a parlé à ReactConf. Je me demande si Om Next le fait ?

Oui, il a suggéré le même genre de chose lors de la première ReactConf. Je n'ai pas vu le dernier Om.next ni entendu le discours d'EuroClojure, mais auparavant, Om utilisait sa propre structure sur laquelle les utilisateurs devaient s'appuyer pour ce faire.

Cette méthode est mon piratage pour parcourir l'arbre, et donc ma question est de savoir comment le fait d'avoir une API publique pour faire ce genre de chose permettrait l'innovation ici dans le "chargement de données latéral", ce qui, je pense, se résume à "un moteur de rendu qui ne ne fonctionne pas de haut en bas"

Je pense que cela équivaut à peu près au rendu superficiel trouvé dans les utilitaires de test - j'ai mentionné en faire une API de première classe en tant que pair du rendu DOM & String à @sebmarkbage chez ReactEurope - les changements 0.14 semblent bien ouvrir la voie à cela.

La réaction initiale est qu'il pourrait être un peu bas - mais de la même manière que les éléments Web extensibles, je pense que cela faciliterait l'expérimentation dans l'espace utilisateur.

Je suis d'accord, si nous avons un moyen de rendre l'arbre, nous pouvons le parcourir et trouver tous les besoins en données et les transmettre.

L'accès à l'ensemble de l'arborescence DOM virtuelle est une fonctionnalité incroyablement puissante et utile à laquelle j'aimerais avoir accès, même si cela est traité comme un problème complètement distinct.
Je pense que cela a beaucoup de sens compte tenu du chemin que React emprunte avec 0.14 et plus.

Compte tenu de la complexité des observables, je suggère humblement aux gentils messieurs de ce fil de regarder la mise en œuvre des données réactives par Meteor : https://github.com/meteor/meteor/wiki/Tracker-Manual. Le réconcilier avec React prend littéralement 3 à 5 lignes de code dans la méthode componentWillMount , quelque chose comme :

  componentWillMount() {
    if (typeof this.getState === 'function') {
      Tracker.autorun(() => {
        // Assuming this.getState() calls some functions that return
        // reactive data sources
        this.setState(this.getState());
      });
    }
  }

Je ne sais pas si Tracker (qui est facilement extrait en tant que bibliothèque distincte) évite le besoin d'un support observable dans React, mais cela semble bien être le cas.

MOBservable suit un modèle très similaire pour actualiser un composant latéralement une fois que certaines valeurs observables sont modifiées, il semble donc que les méthodes de cycle de vie actuelles + les décorateurs offrent suffisamment de flexibilité pour que les bibliothèques tierces expriment ce type de modèles et à mon humble avis un troisième concept de source de données ne ferait que compliquer les choses.

    componentWillMount: function() {
        var baseRender = this.render;
        this.render = function() {
            if (this._watchDisposer)
                this._watchDisposer();
            var[rendering, disposer] = mobservableStatic.watch(() => baseRender.call(this), () => {
                    this.forceUpdate();
            });
            this._watchDisposer = disposer;
            return rendering;
        }
    },

@Mitranim je suis d'accord, c'est une très bonne lecture, merci de trouver ça! C'est efficacement ce que https://github.com/facebook/react/pull/3920 suggère.

Nous ne savons pas quelle proposition est la meilleure. Après avoir joué avec les deux, je suis surtout convaincu que la programmation réactive (comme vous êtes liée; https://github.com/meteor/meteor/wiki/tracker-manual) est la voie à suivre, mais nous n'avons pas atteint le consensus et nous essayons toujours de comprendre ce qui a le plus de sens, donc les commentaires sont les bienvenus.

@Mitranim @jimfb Je suis un grand fan de Meteor depuis plusieurs années maintenant. Tracker est vraiment cool et très fascinant. J'ai créé une présentation démontrant le fonctionnement d'une version très simple de tracker :

https://github.com/ccorcos/meteor-track/blob/master/client/main.js

Et j'ai même créé une bibliothèque de flux observables avec Tracker :

https://github.com/ccorcos/meteor-tracker-streams

Mais comme je suis complètement passé à l'utilisation de React au lieu de Blaze, je me suis rendu compte que Tracker est tout simplement trop compliqué et parfois, une simple méthode de publication/abonnement/onChange est juste 100 fois plus facile.

De plus, pour tous les fans de programmation fonctionnelle, Tracker est livré avec des effets secondaires qui ont du sens 99% du temps, mais parfois ils vous attrapent. Voici à quoi cela ressemblerait dans un composant React

componentWillMount: ->
  <strong i="15">@c</strong> = Tracker.autorun =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})
componentWillUnmount: ->
  @c.stop()

Mon point sur les effets secondaires est que c.stop() va arrêter l'abonnement et l'autoroute intérieure également.

@ccorcos Oui, dans https://github.com/facebook/react/pull/3920 Tous les effets secondaires seraient complètement internes à réagir. En fait, React core pourrait les implémenter d'une manière complètement immuable/fonctionnelle qui n'effectuerait aucune mutation. Mais c'est un détail de mise en œuvre, car les effets secondaires ne seraient de toute façon pas visibles de l'extérieur.

Intéressant... Alors pourquoi ne pas simplement utiliser les rappels onChange ? J'ai trouvé que ceux-ci étaient les plus compatibles. Que vous utilisiez Meteor (Tracker), RxJS, Highland.js, etc., vous pouvez toujours l'intégrer de manière triviale avec un rappel d'événement. Et pareil avec un composant d'ordre élevé React.

Ce que j'aime à propos de REDUX, c'est qu'il conserve cette logique de la structure et empêche les composants de réagir comme des fonctions pures.

@ccorcos Le principal problème est que lorsqu'une instance de composant est détruite, tous les "abonnements" doivent être nettoyés. Les auteurs de composants oublient souvent ce nettoyage, créant ainsi une fuite de mémoire. Nous voulons qu'il soit automatique, ce qui le rend plus facile à écrire (moins passe-partout) et moins sujet aux erreurs (le nettoyage est automatique).

@jimfb Exactement. La désinscription automatique est l'une des choses vraiment intéressantes de l'approche de Tracker. Chaque appel à la source de données réactive rétablit l'abonnement, et une fois que le composant arrête d'appeler des données, il n'y a plus rien à nettoyer !

Ouais, mais ce n'est pas vraiment "automatiquement" désabonné. Vous devez appeler c.stop() pour que le composant se démonte. Vous pouvez cependant résumer cela avec un mixin.

componentWillMount: ->
  <strong i="7">@autorun</strong> =>
    @setState({loading: true})
    Meteor.subscribe 'users', => @setState({loading: false})
    Tracker.autorun =>
      @setState({users: Users.find({}, {sort:{name:-1}}).fetch()})

Mais ce n'est vraiment pas différent des autres API. Vous pouvez créer une méthode intégrée qui se désabonne automatiquement pour vous lors du démontage, etc. Écoutez, je suis un grand fan de Meteor. Donc je ne veux pas te dissuader de ça. C'est juste que parfois, je trouve que c'est vraiment difficile d'expliquer ce genre de choses à quelqu'un qui n'y est pas familier. Pendant ce temps, l'utilisation de simples écouteurs / émetteurs d'événements est beaucoup plus simple à comprendre et à mettre en œuvre, et ils ont tendance à être très compatibles avec le système de réactivité que vous souhaitez utiliser...

@ccorcos Nous parlons peut-être de mécanismes différents. La dernière fois que j'ai vérifié, Tracker n'avait pas d'abonnements permanents ; chaque fonction qui établit une dépendance sur une source de données réactive (en y accédant) est réexécutée _une fois_ lorsqu'elle change, et c'est la fin de l'abonnement. S'il accède à nouveau à la source de données, cela rétablit "l'abonnement" pour une autre modification. Etc.

@Mitranim est correct, la sémantique de # 3920 est plus "automatique" que Meteor (la désinscription est vraiment automatique), et beaucoup plus simple car il n'y a littéralement aucune surface d'API dans le cas d'utilisation courant.

@ccorcos @Mitranim Pour une bibliothèque inspirée Tracker / Vue.js prête à l'emploi, vous pouvez essayer Mobservable , il

La dernière fois que j'ai vérifié, Tracker n'avait pas d'abonnement permanent

Les abonnements @Mitranim peuvent être permanents. Regarde ça.

sub = Meteor.subscribe('chatrooms')
# this subscription lasts until...
sub.stop()

Il y a maintenant des choses intéressantes avec Tracker. Regarde ça.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')
# this subscription lasts until...
comp.stop()

Le dernier exemple n'est pas très utile cependant. Jusqu'à ce que nous fassions quelque chose comme ça.

roomId = new ReactiveVar(1)
comp = Tracker.autorun ->
  Meteor.subscribe('messages', roomId.get())
# when I change the roomId, the autorun will re-run
roomId.set(2)
# the subscription to room 1 was stopped and now the subscription to room 2 has started
# the subscription is stopped when I call stop...
comp.stop()

chaque fonction qui établit une dépendance sur une source de données réactive (en y accédant) est réexécutée une fois lorsqu'elle change, et c'est la fin de l'abonnement.

L'abonnement dure jusqu'à ce que l'exécution automatique soit réexécutée (une dépendance réactive modifiée) ou que le calcul dans lequel il réside s'arrête. Les deux appellent le crochet calcul.onInvalidate.

Voici une version super artificielle qui a accompli exactement la même chose. J'espère que cela vous aidera à comprendre le fonctionnement de Tracker. Et peut-être que vous verrez aussi à quel point cela peut devenir désordonné.

comp = Tracker.autorun ->
  Meteor.subscribe('chatrooms')

# is the same as

comp = Tracker.autorun (c) ->
  sub = null
  Tracker.nonreactive ->
    # dont let comp.stop() stop the subscription using Tracker.nonreactive
    sub = Meteor.subscribe('chatrooms')
  c.onInvalidate ->
    # stop the subscription when the computation is invalidated (re-run)
    sub.stop()
# invalidate and stop the computation
comp.stop()

@ccorcos Je vois maintenant la source de confusion ; c'était mon vocabulaire. Lorsque je parlais d'abonnements, je ne parlais pas des _abonnements Meteor_. Ils déterminent quelles données sont transmises du serveur au client, mais ne sont pas pertinentes pour les mises à jour de la couche d'affichage, qui font l'objet de cette discussion. En disant _abonnement_, je faisais un parallèle entre les écouteurs d'événements traditionnels et la capacité de Tracker à réexécuter une fonction qui dépend d'une source de données réactive. Dans le cas des composants React, il s'agirait d'une méthode de composant qui récupère les données, puis appelle setState ou forceUpdate .

@mweststrate Merci pour l'exemple, ça a l'air intéressant.

Ah oui. Ils ont donc une façon intelligente de le faire.

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    let sub = Meteor.subscribe('messages')
    return {
      loading: !sub.ready(),
      messages: Messages.find().fetch()
    }
  })
componentWillUnmount: function() {
  this.comp.stop()
}

L'abonnement se réabonnera simplement à chaque fois sans aucun problème. sub.ready et Messages.find.fetch sont tous deux "réactifs" et déclencheront l'exécution automatique à chaque fois qu'ils changent. Ce qui est cool avec Tracker, c'est quand vous commencez à masquer les exécutions automatiques et que vous avez juste dans la documentation qu'une certaine fonction se trouve dans un "contexte réactif"

tu pourrais mettre ça dans un mixin

componentWillMount: function() {
  this.comp = Tracker.autorun(() => {
    return this.getReactiveData()
  })
componentWillUnmount: function() {
  this.comp.stop()
}

Et puis vous vous retrouvez avec cette fonction magiquement réactive qui fonctionne tout simplement !

getReactiveData: function() {
  let sub = Meteor.subscribe('messages')
  return {
    loading: !sub.ready(),
    messages: Messages.find().fetch()
  }
}

Tracker est plutôt cool comme ça...

@ccorcos Il s'avère que je me suis quelque peu trompé sur les désinscriptions automatiques lors de l'utilisation d'événements de style Tracker. Vous devez toujours arrêter l'écouteur dans componentWillUnmount , sinon il continuera d'atteindre les sources de données et d'appeler setState (sauf si vous vérifiez avec isMounted() qui est désormais obsolète).

En passant, vous pouvez créer des décorateurs élégants pour rendre les méthodes de composant réactives. Voici quelques exemples : [1] , [2] . Ressemble à ça:

export class Chat extends React.Component {
  <strong i="13">@reactive</strong>
  updateState () {
    this.setState({
      auth: auth.read(),
      messages: messages.read()
    })
  }

  /* ... */
}

@Mitranim assez soigné - je préfère cependant les fonctions de haut niveau. ;)

@sebmarkbage @jimfb Je suis ce fil et le fil

@oztune Aucune mise à jour ; nous nous sommes concentrés sur d'autres priorités. Nous publierons sur l'un des fils de discussion lorsqu'il y aura une mise à jour sur ce sujet.

Les gars, j'ai créé une API universelle pour composer des conteneurs. Vérifiez réagir-komposer . Ce qui, nous pourrions créer des conteneurs avec une fonction d'ordre supérieur.

import { compose } from `react-komposer`;

// Create a component to display Time
const Time = ({time}) => (<div>{time}</div>);

// Create the composer function and tell how to fetch data
const composerFunction = (props, onData) => {
    const handler = setInterval(() => {
    const time = new Date().toString();
    onData(null, {time});
  }, 1000);

  const cleanup = () => clearInterval(handler);
  return cleanup;
};

// Compose the container
const Clock = compose(composerFunction)(Time);

// Render the container
ReactDOM.render(<Clock />, document.getElementById('react-root'));

Voici la version live : https://jsfiddle.net/arunoda/jxse2yw8/

Nous avons également des moyens simples de composer des conteneurs avec Promises, Rx.js Observables et With Meteor's Tracker .

Consultez également mon article à ce sujet : Composons des conteneurs React

@arunoda Nous avons fini par faire quelque chose de très similaire. Une chose que je me demande, c'est comment empêcher composerFunction d'être appelé à chaque changement d'accessoire ?

@oztune En fait, il fonctionnera à nouveau. Nous utilisons ce Lokka et Meteor. Les deux ont des caches locaux et n'atteignent pas le serveur même lorsque nous appelons le composerFunction plusieurs fois.

Mais je pense qu'on pourrait faire quelque chose comme ça :

const options =  {propsToWatch: ["postId"]};
const Clock = compose(composerFunction, options)(Time);

Des idées?

@arunoda C'est ce que nous avons essayé aussi, mais cela crée un peu de déconnexion entre l'action et ses dépendances. Nous faisons maintenant quelque chose de similaire à react-async, où, au lieu d'exécuter immédiatement l'action donnée, composerFunction renverrait une fonction et une clé. Si la clé est différente de la clé précédente renvoyée par composerFunction, la nouvelle fonction sera exécutée. Je ne sais pas s'il s'agit d'une tangente de ce fil github, donc je serais heureux de continuer sur Twitter (même nom d'utilisateur).

@oztune J'ai créé un nouveau numéro GH et continuons notre discussion là-bas. Bien mieux que Twitter je suppose.

Pourquoi ne pas simplement laisser JSX comprendre et rendre directement Observables afin que je puisse passer Observable dans les accessoires, par exemple this.props.todo$ et les intégrer dans JSX. Ensuite, je n'aurais besoin d'aucune API, le reste est géré en dehors de React et HoC est utilisé pour remplir les observables. Cela ne devrait pas avoir d'importance si les accessoires contiennent des données simples ou un observable, donc aucun this.data spécial n'est nécessaire.

{this.props.todo$

De plus, le rendu React pourrait être en mesure de rendre Oservable [JSX], ce qui permettrait la conception décrite dans les liens sans bibliothèque supplémentaire.

https://medium.com/@milankinen/containers-are-dead-long-live-observable-combinators-2cb0c1f06c96#.yxns1dqin

https://github.com/milankinen/react-combinators

Bonjour,
Pour l'instant, nous pourrions facilement utiliser des composants sans état avec des flux rxjs.
Je ne comprends pas la nécessité d'une autre API.
J'ai écrit un exemple - un tableau sur lequel vous pouvez déplacer la souris et quand il atteint 26, il change pour redémarrer.
J'aimerais entendre ce que vous pensez de cette façon.
Voici le code :
https://jsfiddle.net/a6ehwonv/74/

@giltig : J'apprends de cette façon ces derniers temps aussi et j'aime ça. Avec Cycle.js.

J'apprécierais de pouvoir écouter facilement les gestionnaires d'événements définis sur les composants sans avoir à passer dans les sujets pour le pontage. Si j'ai bien compris, c'est à peu près l'inverse de ce qui est suggéré ici. Alternativement, si nous pouvions observer React vdom pour ses événements synthétiques, peut-être que "refs" pourrait être utilisé pour observer afin d'éviter d'avoir des balises CSS juste pour observer.

D'autres RxJ pourraient prendre en charge l'objet dans combineLatest afin que nous puissions utiliser directement les composants fonctionnels React pour créer des composants plus volumineux.

const myFancyReactComponent = ({surface, number, gameover}) => (
        <div> 
          {gameover ? gameover : surface}
          {number}
        </div>
)

const LiveApp = Rx.Observable.combineLatest(
    LiveSurface, DynamicNumberView, DynamicGameOver,
    myFancyReactComponent
)

Salut,
La raison pour laquelle nous n'utilisons pas Cycle ou tout autre framework est que je préfère les bibliothèques aux frameworks (plus de contrôle pour le développeur) et je veux aussi profiter du pouvoir communautaire de React.
Tant de moteurs de rendu sont maintenant développés pour React et il est dommage de ne pas l'utiliser (ReactNative, ReactDom, ReactThree etc..). Il est assez simple d'utiliser uniquement Rxjs et de réagir comme dans l'exemple que j'ai montré ci-dessus.

Cela aurait facilité la réflexion si les composants de réaction pouvaient accepter des pojos aussi longtemps que des observables comme accessoires. Pour l'instant, ce n'est pas possible, donc ce que j'ai montré ci-dessus est la voie que nous avons choisie.

BTW ce que vous avez fait avec MyFancyReactComponent est possible et nous l'avons fait aussi dans certains cas bien que vous puissiez également écrire le jsx directement.

En ce qui concerne les sujets - je pense que c'est un moyen valable car finalement dans le composant React, j'utilise simplement une fonction de gestionnaire qui pourrait être n'importe quoi. Je choisis de l'implémenter avec un sujet à l'intérieur qui reçoit des événements mais quelqu'un d'autre peut choisir autrement - c'est flexible.

Cela aurait facilité la réflexion si les composants de réaction pouvaient accepter des pojos aussi longtemps que des observables comme accessoires. Pour l'instant, ce n'est pas possible, donc ce que j'ai montré ci-dessus est la voie que nous avons choisie.

les accessoires observables n'ont aucun sens à long terme. Ça n'a aucun sens dans ce contexte, en fait...

Il me semble que nous nous sommes retrouvés avec un modèle différent avec Suspense (cache + contexte). Le cache lui-même peut être pris en charge pour les abonnements. Vous pouvez suivre le travail restant pour Suspense sur https://github.com/facebook/react/issues/13206.

Nous proposons également une formule d' abonnement pour les cas plus isolés.

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