Comment créer un flux en temps réel avec Phoenix et React

Ce que vous allez créer

Dans ce tutoriel, je vais vous montrer comment utiliser la puissance de React et de Phoenix pour créer une application de flux qui se mettra à jour en temps réel à mesure que nous ajouterons de nouveaux flux à notre base de données..

introduction

Elixir est reconnu pour sa stabilité et ses fonctionnalités en temps réel. Phoenix exploite également la capacité d'Erlang VM à gérer des millions de connexions, en plus de la superbe syntaxe et des outils productifs d'Elixir. Cela nous aidera à générer la mise à jour en temps réel des données via des API qui seraient utilisées par notre application React pour afficher les données sur l'interface utilisateur..

Commencer

Elixir, Erlang et Phoenix devraient être installés. Vous trouverez plus d'informations à ce sujet sur le site Web du framework Phoenix. En dehors de cela, nous utiliserons un passe-partout React car il est bien entretenu et correctement documenté..

Préparer les API

Dans cette section, nous allons amorcer notre application Phoenix uniquement basée sur des API et ajouter des canaux pour mettre à jour les API en temps réel. Nous allons simplement travailler avec un flux (il contiendra un titre et une description), et une fois que sa valeur aura été modifiée dans la base de données, l'API enverra la valeur mise à jour à notre application frontale..

Démarrer l'appli

Commençons par démarrer l'application Phoenix.

mélanger phoenix.new realtime_feed_api --no-html --no-brunch

Cela créera une application Phoenix nue dans un dossier nommé realtime_feed_api. le --non-html Cette option ne créera pas tous les fichiers statiques (ce qui est utile si vous créez une application avec une API uniquement), et le --pas de brunch L'option n'inclut pas le bundler statique de Phoenix, Brunch. Assurez-vous d’installer les dépendances à l’invite..

Entrons dans le dossier et créons notre base de données.

cd realtime_feed_api

Nous devrons enlever le Nom d'utilisateur et mot de passe champs de notre config / dev.exs fichier puisque nous allons créer notre base de données sans nom d’utilisateur ni mot de passe. Ceci est juste pour garder les choses simples pour ce post. Pour votre application, assurez-vous de créer d'abord une base de données, avec un nom d'utilisateur et un mot de passe..

mélanger ecto.create

La commande ci-dessus va créer notre base de données. Maintenant, nous pouvons exécuter notre serveur Phoenix et tester si tout va bien à ce stade.

mélanger phoenix.server

La commande ci-dessus va déclencher notre serveur Phoenix, et nous pouvons aller à http: // localhost: 4000 pour le voir fonctionner. Actuellement, il lancera un aucun itinéraire trouvé erreur puisque nous n'avons pas encore créé d'itinéraires!

N'hésitez pas à vérifier vos modifications avec mon commit.

Ajouter le modèle de flux

Dans cette étape, nous ajouterons notre Alimentation modèle à notre application Phoenix. Le modèle Feeds consistera en un Titre et un la description.

mix phoenix.gen.json Flux du document: description de la chaîne: chaîne

La commande ci-dessus générera notre Alimentation modèle et contrôleur. Il va également générer les spécifications (que nous ne modifierons pas dans ce tutoriel, juste pour le garder court).

Vous devez ajouter le / flux route dans votre web / routeur.ex déposer à l'intérieur du api portée:

ressources "/ feeds", FeedController, sauf: [: new,: edit]

Nous aurions également besoin de lancer la migration pour créer le les flux table dans notre base de données:

mélanger ecto.migrate

Maintenant, si nous allons à http: // localhost: 4000 / api / feeds, nous verrons que l’API nous envoie une réponse vide car il n’ya pas de données dans notre les flux table.

Vous pouvez vérifier mon commit pour référence.

Ajouter la chaîne de flux

Dans cette étape, nous ajouterons notre Alimentation canal vers notre application Phoenix. Les canaux fournissent un moyen de communication bidirectionnelle à partir des clients qui s’intègrent au Phoenix.PubSub couche pour fonctionnalité en temps réel.

mélanger phoenix.gen.channel

La commande ci-dessus générera un feed_channel.ex déposer à l'intérieur du web / canaux dossier. Grâce à ce fichier, notre application React échangera les données mises à jour de la base de données à l’aide de sockets..

Nous devons ajouter le nouveau canal à notre web / canaux / user_socket.ex fichier:

canal "flux", RealtimeFeedApi.FeedChannel

Comme nous ne faisons aucune authentification pour cette application, nous pouvons modifier notre web / canaux / feed_channel.ex fichier. Nous aurons besoin d'un joindre méthode permettant à notre application React de rejoindre notre canal d’alimentation, un gérer des méthode pour pousser la charge via une connexion socket, et un broadcast_create méthode qui diffusera une charge chaque fois qu'un nouveau flux est créé dans la base de données.

def join ("flux", charge utile, socket) do : ok, "Flux joints", socket end
def handle_out (évènement, données utiles, socket) ne pas enfoncer socket, événement, données utiles : noreply, socket end
def broadcast_create (feed) do payload =% "id" => to_string (feed.id), "title" => feed.title, "description" => feed.description RealtimeFeedApi.Endpoint.broadcast ("feeds", "app / FeedsPage / HAS_NEW_FEEDS", charge utile) fin

Les trois méthodes sont définies ci-dessus. dans le broadcast_create méthode, nous utilisons app / FeedsPage / HAS_NEW_FEEDS étant donné que nous allons utiliser cela comme constante pour notre conteneur d'état Redux, il sera chargé d'informer l'application frontale de la présence de nouveaux flux dans la base de données. Nous en discuterons lorsque nous construirons notre application front-end.

À la fin, nous n’aurons qu’à appeler le broadcast_change méthode par notre feed_controller.ex déposer chaque fois que de nouvelles données sont insérées dans notre créer méthode. Notre créer méthode ressemblera à quelque chose comme:

def create (conn,% "feed" => feed_params) do changeset = Feed.changeset (% Feed , feed_params) cas Repo.insert (changeset) do : ok, feed -> RealtimeFeedApi.FeedChannel.broadcast_create (feed) conn |> put_status (: created) |> put_resp_header ("location", feed_path (conn,: show, feed)) |> render ("show.json", feed: feed) : erreur, changeset - > conn |> put_status (: unprocessable_entity) |> render (RealtimeFeedApi.ChangesetView, "error.json", changeset: changeset) end end

le créer méthode est responsable de l’insertion de nouvelles données dans la base de données. Vous pouvez vérifier mon commit pour référence.

Ajouter le support CORS pour l'API

Nous devons implémenter ce support car, dans notre cas, l'API est servie à partir de http: // localhost: 4000 mais notre application frontale sera exécutée sur http: // localhost: 3000. L'ajout du support CORS est facile. Nous aurons juste besoin d’ajouter cors_plug à notre mix.exs fichier:

defp deps [… : cors_plug, "~> 1.3"] end

Maintenant, nous arrêtons notre serveur Phoenix en utilisant Control-C et récupérez la dépendance à l'aide de la commande suivante:

mélanger deps.get

Nous devrons ajouter la ligne suivante à notre lib / realtime_feed_api / endpoint.ex fichier:

plug CORSPlug

Vous pouvez vérifier mon commit. Nous avons terminé avec tous nos changements en arrière-plan. Passons maintenant à l'application front-end.

Mettre à jour les données frontales en temps réel

Comme mentionné précédemment, nous utiliserons react-boilerplate pour commencer à utiliser notre application front-end. Nous utiliserons la saga Redux qui écoutera nos actions envoyées et, sur cette base, l'interface utilisateur mettra à jour les données.. 

Étant donné que tout est déjà configuré dans le standard, nous n’avons pas à le configurer. Cependant, nous utiliserons les commandes disponibles dans le passe-partout pour échafauder notre application. Commençons par cloner le référentiel:

Git Clone https://github.com/react-boilerplate/react-boilerplate.git realtime_feed_ui

Démarrer l'appli

Maintenant, nous devrons aller à l’intérieur du realtime_feed_ui dossier et installer les dépendances.

cd realtime_feed_ui && npm configuration de l'exécution

Ceci initialise un nouveau projet avec ce standard, supprime le réactif git historique, installe les dépendances et initialise un nouveau référentiel.

Supprimons maintenant l’exemple d’application fourni par la plate-forme standard et remplaçons-le par la plus petite quantité de code standard requise pour commencer à écrire notre application:

npm run clean

Nous pouvons maintenant démarrer notre application en utilisant npm run start et le voir fonctionner à http: // localhost: 3000 /.

Vous pouvez vous référer à mon commit.

Ajouter les conteneurs nécessaires

Dans cette étape, nous allons ajouter deux nouveaux conteneurs, FeedsPage et AddFeedPage, à notre application. le FeedsPage conteneur affichera une liste de flux, et le AddFeedPage conteneur nous permettra d’ajouter un nouveau flux à notre base de données. Nous allons utiliser les générateurs réactifs pour créer nos conteneurs.

npm run générer un conteneur

La commande ci-dessus est utilisée pour échafauder un conteneur dans notre application. Après avoir tapé cette commande, il vous demandera le nom du composant, qui sera FeedsPage dans ce cas, et nous utiliserons le Composant option à l'étape suivante. Nous n'aurons pas besoin d'en-têtes, mais nous aurons besoin actions / constantes / sélecteurs / réducteur aussi bien que sagas pour nos flux asynchrones. Nous n'avons pas besoin messages i18n pour notre application. Nous devrons également suivre une approche similaire pour créer notre AddFeedPage récipient.

Maintenant, nous avons un tas de nouveaux fichiers à travailler. Cela nous fait gagner beaucoup de temps. Sinon, nous devrions créer et configurer tous ces fichiers nous-mêmes. En outre, le générateur crée des fichiers de test, ce qui est très utile, mais nous n'écrirons pas de tests dans le cadre de ce tutoriel..

Ajoutons rapidement nos conteneurs à notre routes.js fichier:

chemin: '/ feeds', nom: 'feedsPage', getComponent (nextState, cb) const importModules = Promise.all ([import ('conteneurs / FeedsPage / réducteur'), ​​importé ('conteneurs / FeedsPage / sagas') , importation ('containers / FeedsPage'),]); const renderRoute = loadModule (cb); importModules.then (([réducteur, sagas, composant]) => injectReducer ('feedsPage', réducteur.default); injectSagas (sagas.default); renderRoute (composant);); importModules.catch (errorLoading); ,

Cela va ajouter notre FeedsPage conteneur à notre / flux route. Nous pouvons le vérifier en visitant http: // localhost: 3000 / feeds. Actuellement, il sera totalement vide car nous n'avons rien dans nos conteneurs, mais il n'y aura pas d'erreur dans la console de notre navigateur..

Nous ferons de même pour notre AddFeedPage récipient.

Vous pouvez vous référer à mon commit pour tous les changements.

Construire la page de liste de flux

Dans cette étape, nous allons construire le FeedsPage qui listera tous nos flux. Afin de garder ce tutoriel petit, nous n’ajouterons aucun style ici, mais à la fin de notre application, je ferai un commit séparé qui ajoutera quelques dessins à notre application..

Commençons par ajouter nos constantes dans notre app / containers / FeedsPage / constants.js fichier:

export const FETCH_FEEDS_REQUEST = 'app / FeedsPage / FETCH_FEEDS_REQUEST'; export const FETCH_FEEDS_SUCCESS = 'app / FeedsPage / FETCH_FEEDS_SUCCESS'; export const FETCH_FEEDS_ERROR = 'app / FeedsPage / FETCH_FEEDS_ERROR'; export const HAS_NEW_FEEDS = 'app / FeedsPage / HAS_NEW_FEEDS';

Nous aurons besoin de ces quatre constantes:

  • le FETCH_FEEDS_REQUEST constante sera utilisée pour initialiser notre demande de récupération.
  • le FETCH_FEEDS_SUCCESS la constante sera utilisée lorsque la demande d'extraction est réussie.
  • le FETCH_FEEDS_ERROR la constante sera utilisée lorsque la demande d'extraction est infructueuse.
  • le HAS_NEW_FEEDS constante sera utilisé quand il y a un nouveau flux dans notre base de données.

Ajoutons nos actions dans notre app / containers / FeedsPage / actions.js fichier:

export const fetchFeedsRequest = () => (type: FETCH_FEEDS_REQUEST,); export const fetchFeeds = (flux) => (type: FETCH_FEEDS_SUCCESS, flux,); export const fetchFeedsError = (error) => (type: FETCH_FEEDS_ERROR, erreur,); export const checkForNewFeeds = () => (type: HAS_NEW_FEEDS,);

Toutes ces actions sont explicites. Maintenant, nous allons structurer le Etat initial de notre application et ajouter un réducteur dans notre app / containers / FeedsPage / reducer.js fichier:

const initialState = fromJS (flux: data: List (), ui: chargement: false, erreur: false,,, métadonnées: hasNewFeeds: false,,);

Ce sera l'état initial de notre application (l'état avant le début de la récupération des données). Puisque nous utilisons ImmutableJS, nous pouvons utiliser sa structure de données List pour stocker nos données immuables. Notre fonction de réduction ressemblera à ceci:

function addFeedPageReducer (state = initialState, action) switch (action.type) case FETCH_FEEDS_REQUEST: retourne l'état .setIn (['feeds', 'ui', 'chargement'], true) .setIn (['feeds', ' ui ',' error '], false); case FETCH_FEEDS_SUCCESS: retourne l'état .setIn (['flux', 'données'], action.feeds.data) .setIn (['flux', 'ui', 'chargement'], false) .setIn (['métadonnées' , 'hasNewFeeds'], false); case FETCH_FEEDS_ERROR: retourne l'état .setIn (['feeds', 'ui', 'erreur'], action.error) .setIn (['feeds', 'ui', 'chargement'], false); case HAS_NEW_FEEDS: état de retour .setIn (['métadonnées', 'hasNewFeeds'], true); défaut: état de retour; 

Fondamentalement, ce que nous faisons ici est de changer notre état en fonction de la constante de nos actions. De cette manière, nous pouvons afficher très facilement les chargeurs et les messages d'erreur. Ce sera beaucoup plus clair lorsque nous l'utilisons dans notre interface utilisateur.

Il est temps de créer nos sélecteurs à l'aide de reselect, qui est une bibliothèque de sélecteurs pour Redux. Nous pouvons extraire très facilement des valeurs d’états complexes en utilisant resélection. Ajoutons les sélecteurs suivants à notre app / containers / FeedsPage / selectors.js fichier:

const feeds = () => createSelector (selectFeedsPageDomain (), (titleState) => titleState.get ('feeds'). get ('data')); const error = () => createSelector (selectFeedsPageDomain (), (errorState) => errorState.get ('feeds'). get ('ui'). get ('error_state')); const isLoading = () => createSelector (selectFeedsPageDomain (), (loadingState) => loadingState.get ('feeds'). get ('ui'). get ('loading')); const hasNewFeeds = () => createSelector (selectFeedsPageDomain (), (newFeedsState) => newFeedsState.get ('métadonnées'). get ('hasNewFeeds'));

Comme vous pouvez le voir ici, nous utilisons la structure de notre Etat initial extraire des données de notre état. Vous devez juste vous rappeler la syntaxe de resélectionner.

Il est temps d'ajouter nos sagas en utilisant redux-saga. Ici, l’idée de base est que nous devons créer une fonction pour extraire des données et une autre fonction pour surveiller la fonction initiale, de sorte que chaque fois qu’une action spécifique soit envoyée, nous devons appeler la fonction initiale. Ajoutons la fonction qui va chercher notre liste de flux depuis l’application dorsale dans notre app / containers / FeedsPage / sagas.js fichier:

function * getFeeds () const requestURL = 'http: // localhost: 4000 / api / feeds'; try // Appelez notre assistant de requête (voir 'utils / Request') const feeds = return call (request, requestURL); rendement mis (fetchFeeds (aliments));  catch (err) rendement put (fetchFeedsError (err)); 

Ici, demande est juste une fonction util qui appelle notre API à notre back-end. L'ensemble du fichier est disponible sur react-boilerplate. Nous y ferons un léger changement après avoir terminé notre sagas.js fichier.

Nous devons également créer une fonction supplémentaire pour regarder les getFeeds une fonction:

fonction d'exportation * watchGetFeeds () observateur constant = rendement takeLatest (FETCH_FEEDS_REQUEST, getFeeds); // Suspendre l'exécution jusqu'à ce que les changements d'emplacement donnent le rendement take (LOCATION_CHANGE); rendement annuler (observateur); 

Comme on peut le voir ici, le getFeeds la fonction sera appelée lors de l'envoi de l'action contenant le FETCH_FEEDS_REQUEST constant.

Maintenant, copions le fichier request.js de react-boilerplate dans notre application à l'intérieur du app / utils dossier, puis modifiez le demande une fonction:

demande de fonction d'exportation par défaut (url, method = 'GET', body) return fetch (url, en-têtes: 'Content-Type': 'application / json',, méthode, body: JSON.stringify (body), ) .then (checkStatus) .then (parseJSON);  

Je viens d'ajouter quelques valeurs par défaut qui nous aideront à réduire le code ultérieurement car nous n'avons pas besoin de transmettre la méthode et les en-têtes à chaque fois. Maintenant, nous devons créer un autre fichier util à l’intérieur du app / utils dossier. Nous appellerons ce fichier socketSagas.js. Il contiendra quatre fonctions: connectToSocketjoinChannelcreateSocketChannel, et handleUpdatedData

le connectToSocket fonction sera responsable de la connexion à notre socket API back-end. Nous allons utiliser le phénix paquet npm. Nous devrons donc l'installer:

npm installer phoenix --save

Cela installera le phénix paquet npm et enregistrez-le sur notre package.json fichier. Notre connectToSocket fonction ressemblera à ceci:

fonction d'exportation * connectToSocket () const socket = new Socket ('ws: localhost: 4000 / socket'); socket.connect (); prise de retour; 

Ensuite, nous définissons notre joinChannel fonction, qui sera responsable de rejoindre un canal particulier de notre back-end. le joinChannel fonction aura le contenu suivant:

fonction d'exportation * joinChannel (socket, channelName) const channel = socket.channel (channelName, ); channel.join () .receive ('ok', (resp) => console.log ('Joined succès', resp);) .receive ('error', (resp) => console.log (' Impossible de rejoindre ', resp);); canal de retour; 

Si la jonction est réussie, nous allons enregistrer le message 'Joint avec succès' juste pour le tester. S'il y a eu une erreur pendant la phase de jonction, nous enregistrerons également cela uniquement à des fins de débogage.

le createSocketChannel sera responsable de la création d'un canal d'événement à partir d'un socket donné.

export const createSocketChannel = (canal, constante, fn) => // 'eventChannel' prend une fonction d'abonné // la fonction d'abonné prend un argument 'emit' pour placer des messages sur le canal eventChannel ((emit) => const newDataHandler = (événement) => console.log (événement); emit (fn (événement));; channel.on (constant, newDataHandler); const unsubscribe = () => channel.off (constant, newDataHandler); ; return désinscription;);

Cette fonction sera également utile si l'on veut se désabonner d'un canal particulier.

le handleUpdatedData va simplement appeler une action passée comme argument.

fonction d'exportation * handleUpdatedData (action) return put (action); 

Ajoutons maintenant le reste des sagas dans notre app / containers / FeedsPage / sagas.js fichier. Nous allons créer deux autres fonctions ici: connectWithFeedsSocketForNewFeeds et watchConnectWithFeedsSocketForNewFeeds

le connectWithFeedsSocketForNewFeeds La fonction sera responsable de la connexion avec le socket principal et de la recherche de nouveaux flux. S'il y a de nouveaux flux, il appellera le createSocketChannel fonction de la utils / socketSagas.js fichier, qui créera un canal d’événement pour ce socket donné. Notre connectWithFeedsSocketForNewFeeds fonction contiendra les éléments suivants:

function * connectWithFeedsSocketForNewFeeds () const socket = appel de rendement (connectToSocket); const canal = appel de rendement (joinChannel, socket, 'feeds'); const socketChannel = appel de rendement (createSocketChannel, canal, HAS_NEW_FEEDS, checkForNewFeeds); while (true) action const = rendement prendre (socketChannel); fourchette de rendement (handleUpdatedData, action); 

Et le watchConnectWithFeedsSocketForNewFeeds aura le suivant:

fonction d'exportation * watchConnectWithFeedsSocketForNewFeeds () observateur constant = rendement takeLatest (FETCH_FEEDS_SUCCESS, connectWithFeedsSocketForNewFeeds); // Suspendre l'exécution jusqu'à ce que les changements d'emplacement donnent le rendement take (LOCATION_CHANGE); rendement annuler (observateur); 

Maintenant, nous allons tout attacher à notre app / containers / FeedsPage / index.js fichier. Ce fichier contiendra tous les éléments de notre interface utilisateur. Commençons par appeler le support qui va chercher les données à partir de l'arrière dans notre composantDidMount:

composantDidMount () this.props.fetchFeedsRequest (); 

Cela va chercher tous les flux. Maintenant, nous devons appeler le fetchFeedsRequest prop à nouveau chaque fois que le hasNewFeeds prop est vrai (vous pouvez vous référer à initialState de notre réducteur pour la structure de notre application):

composantWillReceiveProps (nextProps) if (nextProps.hasNewFeeds) this.props.fetchFeedsRequest (); 

Après cela, nous rendons simplement le se nourrit dans notre fonction de rendu. Nous allons créer un feedsNode fonctionne avec le contenu suivant:

feedsNode () return [… this.props.feeds] .reverse (). map ((feed) => // eslint-disable-line retourne un style de corps de flèche ( 

feed.title

feed.description

) );

Et puis, nous pouvons appeler cette méthode dans notre rendre méthode:

render () if (this.props.loading) return ( 
Chargement…
) revenir (
this.feedsNode ()
)

Si nous allons maintenant sur http: // localhost: 3000 / feeds, nous verrons les informations suivantes consignées dans notre console:

Rejoint avec succès rejoint les flux

Cela signifie que notre API de flux fonctionne correctement et que nous avons réussi à connecter notre serveur frontal à notre application dorsale. Maintenant, nous avons juste besoin de créer un formulaire à travers lequel nous pouvons entrer un nouveau flux.

N'hésitez pas à vous référer à mon commit car beaucoup de choses ont été incluses dans ce commit!

Construire le formulaire pour ajouter un nouveau flux

Dans cette étape, nous allons créer un formulaire à travers lequel nous pouvons ajouter un nouveau flux à notre base de données..

Commençons par ajouter les constantes à notre app / containers / AddFeedPage / constants.js fichier:

export const UPDATE_ATTRIBUTES = 'app / AddFeedPage / UPDATE_ATTRIBUTES'; export const SAVE_FEED_REQUEST = 'app / AddFeedPage / SAVE_FEED_REQUEST'; export const SAVE_FEED_SUCCESS = 'app / AddFeedPage / SAVE_FEED_SUCCESS'; export const SAVE_FEED_ERROR = 'app / AddFeedPage / SAVE_FEED_ERROR'; 

le UPDATE_ATTRIBUTES constante sera utilisé lorsque nous ajouterons du texte à la zone de saisie. Toutes les autres constantes seront utilisées pour enregistrer le titre et la description du flux dans notre base de données..

le AddFeedPage conteneur utilisera quatre actions: updateAttributessaveFeedRequestsaveFeed, et saveFeedError. le updateAttributes fonction mettra à jour les attributs de notre nouveau flux. Cela signifie que chaque fois que nous tapons quelque chose dans la zone de saisie du titre et de la description du flux, updateAttributes fonction mettra à jour notre état Redux. Ces quatre actions ressembleront à ceci:

export const updateAttributes = (attributs) => (type: UPDATE_ATTRIBUTES, attributs,); export const saveFeedRequest = () => (type: SAVE_FEED_REQUEST,); export const saveFeed = () => (type: SAVE_FEED_SUCCESS,); export const saveFeedError = (error) => (type: SAVE_FEED_ERROR, error,);

Ensuite, ajoutons nos fonctions de réduction dans app / containers / AddFeedPage / reducer.js fichier. le Etat initial ressemblera à ceci:

const initialState = fromJS (flux: données: titre: ", description:",, ui: enregistrement: faux, erreur: null,,,);

Et la fonction de réduction ressemblera à quelque chose comme:

fonction addFeedPageReducer (state = initialState, action) commutateur (action.type) cas UPDATE_ATTRIBUTES: état renvoyé .setIn (['flux', 'données', 'titre'], action.attributes.title) .setIn ([' feed ',' data ',' description '], action.attributes.description); case SAVE_FEED_REQUEST: retourne l'état .setIn (['feed', 'ui', 'save'], true) .setIn (['feed', 'ui', 'error'], false); case SAVE_FEED_SUCCESS: retourne l'état .setIn (['feed', 'data', 'title'], ") .setIn (['feed', 'data', 'description'],") .setIn (['feed' , 'ui', 'save'], false); case SAVE_FEED_ERROR: retourne l'état .setIn (['feed', 'ui', 'erreur'], action.error) .setIn (['feed', 'ui', 'save'], false); défaut: état de retour; 

Ensuite, nous allons configurer notre app / containers / AddFeedPage / selectors.js fichier. Il aura quatre sélecteurs: Titrela descriptionErreur, et économie. Comme leur nom l’indique, ces sélecteurs extrairont ces états de l’état Redux et les rendront disponibles dans notre conteneur comme accessoires..

Ces quatre fonctions ressembleront à ceci:

const title = () => createSelector (selectAddFeedPageDomain (), (titleState) => titleState.get ('feed'). get ('data'). get ('title')); const description = () => createSelector (selectAddFeedPageDomain (), (titleState) => titleState.get ('feed'). get ('data'). get ('description')); const error = () => createSelector (selectAddFeedPageDomain (), (errorState) => errorState.get ('feed'). get ('ui'). get ('error_state')); const saving = () => createSelector (selectAddFeedPageDomain (), (SavingState) => SavingState.get ('feed'). get ('ui'). get ('saving'));

Ensuite, configurons nos sagas pour AddFeedPage récipient. Il aura deux fonctions: saveFeed et regarderSaveFeed. le saveFeed fonction sera responsable de faire la POSTER demande à notre API, et il aura les éléments suivants:

fonction d'exportation * saveFeed () const title = rendement select (feedTitle ()); const description = rendement select (feedDescription ()); const requestURL = 'http: // localhost: 4000 / api / feeds'; try // Appelez notre assistant de requête (voir 'utils / Request') return put (saveFeedDispatch ()); appel de rendement (request, requestURL, 'POST', feed: title, description,,,);  catch (err) rendement mis (saveFeedError (err)); 

le regarderSaveFeed fonction sera similaire à nos fonctions de veille précédentes:

fonction d'exportation * watchSaveFeed () observateur constant = rendement takeLatest (SAVE_FEED_REQUEST, saveFeed); // Suspendre l'exécution jusqu'à ce que les changements d'emplacement donnent le rendement take (LOCATION_CHANGE); rendement annuler (observateur); 

Ensuite, nous devons simplement rendre le formulaire dans notre conteneur. Pour que les choses restent modularisées, créons un sous-composant pour la fiche. Créer un nouveau fichier form.js à l'intérieur de notre app / containers / AddFeedPage / sous-composants dossier (le sous-composants dossier est un nouveau dossier que vous devrez créer). Il contiendra le formulaire avec une zone de saisie pour le titre du flux et une zone de texte pour la description du flux. le rendre méthode aura le contenu suivant:

render () return (