Redux: Comment appeler des actions sans utiliser @connect?

Créé le 30 juil. 2015  ·  32Commentaires  ·  Source: reduxjs/redux

J'ai un conteneur d'en-tête qui transmet les données d'image (URL, taille) à un composant d'en-tête en utilisant

@connect(state => ({
  header: state.header
}))
export default class HeaderContainer extends Component {
 /// pass props.header data to child Header which renders image.
}

Ensuite, j'ai un autre composant qui doit appeler des actions d'en-tête afin de mettre à jour l'état de l'en-tête. Il n'utilise pas l'état, il déclenche simplement des actions. Afaik j'ai besoin de faire quelque chose comme ça:

const { dispatch } = this.props
const actions = bindActionCreators(headerActions, dispatch)

Le problème est que this.props.dispatch ne semble disponible que lorsque vous utilisez le décorateur @connect . Le deuxième composant ne s'intéresse pas du tout à l'état de l'en-tête. Dois-je vraiment utiliser connect ici ou peut-il lier les actions d'une manière différente? Il me semble mal d'utiliser @connect si je n'utilise pas les données.

question

Commentaire le plus utile

Vous pouvez passer dispatch à ce composant manuellement comme accessoire.

Tous les 32 commentaires

Je ne suis pas sûr que ce soit une "bonne" façon ou non, mais vous pouvez accéder au magasin via le contexte. Voir le composant Connector par exemple:
https://github.com/gaearon/react-redux/blob/master/src/components/createConnector.js

Je ne te suis pas. J'accède parfaitement au magasin en utilisant @connect. Je recherche un moyen de lier / appeler des actions dans un composant sans utiliser @connect.

La fonction dispatch est une méthode du magasin. Si vous en avez besoin dans votre composant mais que vous ne souhaitez pas utiliser @connect , vous pouvez accéder au magasin via le contexte et utiliser sa méthode de répartition:

class MyComponent extends Compoenent {
    static contextTypes = {
        store: React.PropTypes.object.isRequired,
    }

    render() {
         const { dispatch } = this.context.store
         const actions = bindActionCreators(headerActions, dispatch)
    }
}

Vous devez transmettre l'envoi depuis le composant parent ou l'action déjà liée à l'envoi. C'est la façon dont je pense que le contexte fonctionne également, même si c'est une solution de contournement

Merci d'avoir expliqué cela! Je comprends maintenant.

Vous pouvez passer dispatch à ce composant manuellement comme accessoire.

Je suis également un peu perplexe face à ce problème.

Si je comprends bien, nous devons soit avoir dispatch disponible via les accessoires d'un composant (pour pouvoir distribuer des actions à partir de ce composant) ou donner des créateurs d'action déjà liés à dispatch , à partir d'un composant parent. Mais les deux solutions nécessitent d'avoir un composant parent qui est connect ed pour que la répartition soit mise à sa disposition via ses accessoires.

Donc, à la fin, nous transférons simplement notre dépendance de dispatch sur un composant parent. Et comment feriez-vous cela s'il n'y a pas de parent connect ed?

Par exemple, que se passerait-il si nous étions dans une hiérarchie profonde de composants:

Provider
  Router
    AppContainer
     ...
       ...
         ...
           TheComponentThatNeedDispatch

et si aucun des parents n'a besoin de se connecter à l'état redux?

La mise à disposition de la répartition vers un composant en tant qu'effet secondaire de l'utilisation de connect semble nous mettre dans une situation où il est pré-requis d'avoir un de ses parents pour être connect ed pour être capable de dispatch une action. Cela semble étrange car il ne devrait y avoir aucune corrélation entre la capacité d'un composant à dispatch une action et la nécessité d'utiliser connect pour lire à partir de l'état redux.

Je sais que nous pourrions toujours accéder à dispatch partir d'un contexte de composant (via store.dispatch ) mais puisque ce n'est pas la méthode recommandée ...

Qu'est-ce que tu en penses?

Merci!

Cela semble étrange car il ne devrait y avoir aucune corrélation entre la capacité d'un composant à envoyer une action et la nécessité d'utiliser connect pour lire à partir de l'état redux.

Pourquoi est-ce bizarre? Pensez à connect comme «se connecter à Redux» et pas simplement «se connecter à une partie de l'état de Redux». Ensuite, cela a du sens: pour pouvoir dispatch soit le composant lui-même, soit un de ses parents, doit être connect() ed à Redux.

Avec l'API React Redux actuelle, il n'est pas pratique de se connecter à Redux sans sélectionner également l'état. Vous devrez écrire quelque chose comme connect(state => ({}))(MyComponent) (retourner un objet vide). C'est pourquoi nous avons exploré pour cela dispatch un citoyen de première classe et prévoyons d'ajouter une API pour récupérer «juste les dispatch ».

Est-ce que ça a du sens?

Pensez à la connexion comme «connexion à Redux» et pas simplement «connexion à une partie de l'état de Redux».

C'est tout à fait logique maintenant :).

J'ai hâte d'utiliser la nouvelle API .

Merci beaucoup, utiliser Redux est un bonheur!

Cela a l'air génial!

@gaearon hey Dan pouvez-vous me donner un conseil sur la façon d'utiliser bindActionCreators avec des composants imbriqués:

"AddProduct" - connecté à redux (voir ci-dessous)
__ "Liste de produits"
____ "ProductView".

Je souhaite que ProductView puisse expédier, mais seul le maître "Ajouter un produit" a:

function appState(state) {
    return {...};
}

export default connect(appState)(AddProduct)

Vous pouvez soit connect() le composant imbriqué aussi, soit lui passer dispatch comme accessoire.

merci Dan, j'apprécie vraiment ton aide. aime vraiment redux aussi!

une autre question si je peux:
puis-je placer des demandes de publication de l'API du serveur depuis l'intérieur du réducteur lui-même?
quand je POST dans la base de données (mongodb), l'élément obtient un "_id" que je voudrais avoir dans mon état redux. l'envoi doit-il être placé comme rappel pour la demande POST?

Je vous remercie

puis-je placer des demandes de publication de l'API du serveur depuis l'intérieur du réducteur lui-même?

Certainement pas, les réducteurs doivent être des fonctions pures exemptes d'effets secondaires.

quand je POST dans la base de données (mongodb), l'élément obtient un "_id" que je voudrais avoir dans mon état redux. l'envoi doit-il être placé comme rappel pour la demande POST?

Oui.

Veuillez consulter ce guide: http://rackt.github.io/redux/docs/advanced/AsyncActions.html

merci Dan, fonctionne très bien (y)

si j'utilise le rendu du serveur pour permettre à l'utilisateur actuel de se connecter,
puis-je ignorer l'extraction asynchrone du magasin et le faire sur le serveur?

si j'utilise le rendu du serveur pour disons que l'utilisateur actuel est connecté, puis-je ignorer la récupération asynchrone du magasin et le faire sur le serveur?

Vous pouvez créer un magasin sur le serveur, attendre la fin des actions asynchrones, puis effectuer le rendu.
Voir aussi http://rackt.github.io/redux/docs/recipes/ServerRendering.html

Il me manque probablement quelque chose de critique, mais pourquoi ne pas appliquer partiellement la répartition à tous les créateurs d'action lors de l'initialisation de l'application? Ensuite, vous pouvez appeler de n'importe où sans vous soucier de savoir comment obtenir une référence à expédier.

@mpoisot : parce que vous devez connaître, importer, lier et distribuer _ chaque_ créateur d'action potentiel au démarrage de l'application. Entre autres choses, cela n'aide pas dans des situations comme le fractionnement de code.

@markerikson Je dois déjà connaître tous les réducteurs à app init pour pouvoir appeler createStore . J'imagine appeler bindAllActionCreators(store.dispatch) sur la ligne suivante.

Certes, vous pourriez finir par extraire un peu plus de code que vous n'en utilisez réellement. Mais si c'est un problème, ne le faites pas de cette façon. Ou divisez vos ensembles de réducteurs (réducteur + créateurs d'action + sélecteurs) en ensembles plus petits et plus ciblés. Ou ne liez que certains créateurs d'action.

Y a-t-il une terrible pénalité de performance pour faire cela? Ou cela mène-t-il en quelque sorte sur un chemin terrible vers l'enfer du codage?

@mpoisot : pas vraiment de problèmes de perf auxquels je puisse penser. Cependant, cela limite la testabilité et la réutilisabilité.

Une utilisation correcte de connect conduit à une approche d'injection de dépendance légère. Un composant "simple" sait seulement qu'on lui donne une fonction comme accessoire, et il devrait appeler cette fonction si nécessaire. Il ne se soucie pas vraiment s'il s'agit d'un rappel qui a été transmis directement par un composant parent ou un créateur d'action pré-lié. Cela signifie que le composant peut être réutilisé de manière simple et facilite également le test. D'un autre côté, faire quelque chose comme l'importation directe du magasin aboutit à lier le composant à cette instance et à cette implémentation de magasin spécifiques, c'est pourquoi il est déconseillé (par la FAQ Redux: http://redux.js.org/docs/faq/ StoreSetup.html # store-setup-multiple-stores). Si je comprends bien ce que vous demandez, c'est fondamentalement la même situation que d'importer et de référencer directement le magasin.

Ah bon point, appliquer partiellement la répartition dans les créateurs d'action équivaudrait à importer le magasin en tant que singleton, ce qui est mal vu. J'ai essayé de savoir pourquoi, et j'ai trouvé plusieurs endroits où Dan Abramov a déclaré qu'un magasin singleton était mal vu en raison du rendu côté serveur (que la plupart des gens n'utiliseront jamais), mais étrange, il n'a jamais mentionné les tests (que tout le monde devrait faire, ou du moins se sentir mal de ne pas faire).

Ce qui me fait penser, vous pouvez sûrement créer un objet singleton de magasin dont le magasin peut être mis à jour avec différents magasins pour le test? Au lieu d'exporter directement le magasin lui-même, renvoyez-le via StoreSingleton.getStore() , et changez le magasin actuel via StoreSingleton.setStore() . Donc, en termes de test, vous ne seriez pas lié à une seule instance d'un magasin. Cependant, je peux voir pourquoi cela ne fonctionnerait pas dans un environnement de serveur threadé.

@mpoisot Vous venez de décrire ce que fait Provider / connect. ;)

Souvent, j'ai un composant "stupide" qui un jour a besoin d'être envoyé (ou une action pré-liée). Alors je l'enroule dans une connexion, ou peut-être lui transmet des éléments d'un composant contenant qui est connecté. Cela ressemble souvent à beaucoup de câblage et d'accessoires de passage juste pour éviter une expédition mondialement accessible. Pour mapStateToProps (), il y a de merveilleux gains de performances avec Provider / connect (). Et pour réutiliser des composants stupides, il est tout à fait logique d'extraire la logique d'envoi vers le composant d'ordre supérieur.

Mais j'ai frappé ce scénario assez de fois pour me demander pourquoi. Et il semble que la réponse soit "afin que vous puissiez un jour faire un rendu côté serveur". (Corrigez-moi si j'ai tort, s'il-vous plait).

En ce moment, connect() vous donne juste le dispatch méthode de Provider magasin de, mais à l'avenir, peut - être cela pourrait changer. Nous pourrions ajouter des fonctionnalités supplémentaires, telles que l'intégration avec des outils de développement pour vous dire quel composant React était la source d'une action qui se déclenche, par exemple. Ou quelqu'un pourrait créer un composant qui améliore le magasin d'une autre manière en prenant le magasin dans son contexte, en l'améliorant d'une manière ou d'une autre, puis en passant ce magasin amélioré dans l'arborescence des composants. Si vous avez lié vos créateurs d'action à un magasin singleton, vous appelleriez potentiellement le mauvais dispatch .

Oui, certaines des expériences "composant / état encapsulé" que j'ai vues fonctionnent exactement avec cette approche. La chose intéressante à propos de cette idée est que les autres composants n'ont pas besoin de savoir que quoi que ce soit a changé, car les composants du wrapper saisissent toujours this.context.store et utilisent son dispatch si nécessaire.

J'ai été des deux côtés de ça -

Dans mon application principale, nous traitons le magasin comme un singleton et utilisons une fonction getStore() disponible dans le monde entier. Nous n'utilisons pas de rendu côté serveur. Nous utilisons toujours Provider et connect pour mapStateToProps .

D'un autre côté, j'ai construit une application expérimentale dans laquelle j'emballe le magasin (comme @markerikson mentionnés) à divers endroits dans la hiérarchie des composants, et à la fois dispatch et getState can changer dans toute l'application, ce qui signifie qu'ils _ doivent_ être câblés dans toute l'application (soit via context ou props ) et ne peuvent pas être des singletons.

Selon ce que vous voulez accomplir, je pense que le magasin unique est viable, mais personnellement, je m'en éloigne lentement.

Ouais. Vous _pouvez_ certainement traiter le magasin comme un singleton accessible, mais cela vous limite, ce n'est pas considéré comme idiomatique, et je ne vois aucun avantage réel à le faire.

Merci, bonne rétroaction. Quelqu'un peut-il publier un lien montrant un emballage de magasin?

@mpoisot

Voici l'un des commentaires de Dan qui illustre à quel point l'enveloppement de dispatch pourrait être utile:

https://github.com/reactjs/redux/issues/822#issuecomment -145271384

Vous devrez peut-être lire tout le fil pour mieux comprendre quelle est la motivation.

En particulier, remarquez comment dispatch est enveloppé (je l'ai mis en évidence):

wrappeddispatch

Désolé pour le détournement de cet ancien message alors que je suis confronté à la même confusion, souhaitant expédier une action sans connexion.

export default class App extends Component {
...
  componentDidMount() {
    //registering event listener
    BackgroundGeolocation.on('location', this.onLocation, this.onError);
  }

  onLocation(location) {
   //wishing to dispatch an action to update variable in store
  }

  render() {
    return (
      <Provider store={store}>
        <AlertProvider>
          <MainNavigator screenProps={{ isConnected: this.state.isConnected }} />
        </AlertProvider>
      </Provider>
    );
  }
}

D'après ce que je comprends, je ne peux pas me connecter pour stocker dans ce composant car nous sommes juste en train de configurer et de passer le magasin dans Provider . Comment pourrais-je envoyer une action dans mon événement onLocation ?

@isaaclem : eh bien, dans ce cas précis, vous _do_ avez une référence au magasin, vous pouvez donc appeler directement store.dispatch(someAction) .

L'autre option serait de restructurer la structure de vos composants afin que certains composants _à l'intérieur_ du <Provider> effectuent le comportement de géolocalisation en arrière-plan, et puissent également être connectés.

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