Handle Rails destella los posts después de las llamadas AJAX usando ReactJS

Varias publicaciones describen la visualización de posts flash después de las llamadas AJAX (por ejemplo, Rails no muestra posts flash después de la llamada ajax y ¿Cómo se maneja el flash de Rail con las requestes Ajax? ).

Estoy usando ReactJS con react-rails y quiero hacer esto, y quiero aprovechar la representación dinámica de la pantalla de React.

Así que estoy respondiendo mi propia pregunta sobre cómo renderizar posts flash después de las llamadas AJAX cuando utilizo ReactJS a continuación como una manera de responder a otras personas que puedan encontrar esto y get algún consejo para mejorarlo.

Esta implementación se basa principalmente en las otras publicaciones SO sobre este tema (ver references en cuestión). El código supone Rails 4.x con la canalización de activos, HAML para las vistas, Bootstrap para el layout y el uso de la gem de react-rails .

( actualización : una discusión un poco más larga se puede encontrar en mi blog ).

Diseño
Aquí está el file app/views/layouts/application.html.haml :

 !!! %html %head %title Demo = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags %body %div.container %div#flash_messages = yield 

Tenga en count que el div tiene una identificación de #flash_messages . La location de este contenedor div determina dónde aparece el post flash.

Controlador
La rutina privada siguiente simplemente convierte los usos de la matriz nativa de matrices en JSE parseable en el encabezado.

 class ApplicationController < ActionController::Base protect_from_forgery with: :exception after_filter :flash_to_http_header private def flash_to_http_header return unless request.xhr? return if flash.empty? response.headers['X-FlashMessages'] = flash.to_hash.to_json flash.discard # don't want the flash to appear when you reload page end end 

Javascript (ReactJS)
El siguiente javascript está formado por una class Reaccionar, una function global para procesar los posts instantáneos del encabezado de respuesta y un manejador JQuery para completar las llamadas AJAX. Se coloca en un solo file JX (utilicé la app/assets/javascripts/react/flash_messages.js.jsx ) para mantener juntas la funcionalidad JS relevante. Discuto esto más abajo.

 /** @jsx React.DOM */ var FlashMessages = React.createClass({ getInitialState: function() { return {messages: this.props.messages}; }, messages: function (messageArray) { this.replaceState({messages: messageArray}); }, render: function() { return ( <div className='flash_messages_component'> {this.state.messages.map(function(message, index) { _level = message[0]; _text = message[1]; return ( <div key={index} className={this._flash_class(_level)}> {_text} </div> ); }.bind(this))} </div> ) }, _flash_class: function(level) { var _result = 'alert alert-error'; if (level === 'notice') { _result = 'alert alert-info'; } else if (level === 'success') { _result = 'alert alert-success'; } else if (level === 'error') { _result = 'alert alert-error'; } else if (level === 'alert') { _result = 'alert alert-error'; } return _result; } }); function handleFlashMessagesHeader(node, xhr) { var _message_array = new Array(); var _raw_messages = xhr.getResponseHeader("X-FlashMessages") if (_raw_messages) { var _json_messages = JSON.parse(_raw_messages); count = 0 for (var key in _json_messages) { _message_array[count] = new Array(); _message_array[count][0] = key; _message_array[count][1] = _json_messages[key]; count += 1; } } node.messages(_message_array); } $(document).ready(function() { var dummy = new Array(); var flashDiv = React.render(<FlashMessages messages={dummy} />, $('#flash_messages')[0]); $(document).ajaxComplete(function(event, xhr, settings) { handleFlashMessagesHeader(flashDiv, xhr); }); }); 

La class de FlashMessages hace algunas cosas. Primero, mueve los accesorios al estado. En general, esto sería un anti-patrón, pero al hacerlo, permite que el código que no es de React active cambios cuando sea necesario. La function de messages es ese disparador y debe ser invocado por un código JS externo. El procesamiento principal para render asume la estructura de datos Rails flash-native array-of-2-element-arrays para mantener el procesamiento al mínimo y permitir que el componente se use directamente desde una vista en lugar de solo llamadas AJAX. Finalmente, el método local _flash_class admite el estilo Bootstrap (que, por supuesto, podría ajustarse para otro estilo, según se desee).

handleFlashMessagesHeader es una function global para convertir el JSON a una matriz de matrices realizada por el método de filter del controller Rails. Tenga en count que toma el elemento DOM con el marcador de identificación de la vista / layout de la aplicación Rails.

La última sección se debe ejecutar en la carga de la página y, por lo tanto, depende de JQuery Ready. React.render (formalmente React.renderComponent ) se guarda en una variable global para permitir la llamada directa en el FlashMessages de message del object FlashMessages (la matriz dummy utilizada es solo para sembrar una matriz de flashes vacía cuando se invoca por primera vez, esto probablemente podría manejarse dentro de la class React también por una testing nula). Siempre que se ajaxComplete handleFlashMessageHeader , se invoca la function handleFlashMessageHeader y pasa el object React para su actualización.

No AJAX
Debido a que la class React asume matrices de matrices nativas, la vista simplemente podría haber llamado una

 = react-component 'FlashMessages', flash, :div 

en su lugar, para mostrarlo, pero, por supuesto, es de menor uso ya que la respuesta dinámica no es compatible. Sin embargo, la capacidad de invocarlo de cualquier manera brinda más flexibilidad para algunos casos de uso.

No pude get la respuesta de @rdnewman para trabajar con Rails 5 y react-rails 1.9.0 (corresponde a Reaccionar 15.3.0). Creo que algo cambió en React que no permite el acceso externo a las funciones, por lo que la llamada a .messages() en handleFlashMessagesHeader() no funciona (function no encontrada).

Adapté la respuesta a algo que funciona para mí. Estoy aprendiendo Reaccionar, por lo que puede haber una manera mejor, pero esto es lo que tengo:

app/controllers/application_controller.rb

 class ApplicationController < ActionController::Base protect_from_forgery with: :exception after_action :flash_to_http_header, if: -> { request.xhr? } private def flash_to_http_header return if flash.empty? response.headers['X-FlashMessages'] = flash.to_hash.to_json flash.discard end end 

app/assets/javascripts/components/flash_messages.js.coffee :

 @FlashMessages = React.createClass getInitialState: -> messages: @props.data getDefaultProps: -> messages: {} componentDidMount: -> $(document).ajaxComplete ((event, xhr, settings) -> header_messages = xhr.getResponseHeader('X-FlashMessages') messages = {} if header_messages messages = JSON.parse(header_messages) @replaceState {messages: messages} ).bind(this) messageClass: (messageType) -> if messageType == 'notice' return 'alert alert-warning' if messageType == 'success' return 'alert alert-success' return 'alert alert-error' render: -> React.DOM.div className: 'flash-messages' for messageType of @state.messages React.DOM.div className: @messageClass(messageType) key: messageType @state.messages[messageType] 

app/views/layouts/application.html.erb

 <%= react_component 'FlashMessages', { data: flash.to_hash } %> 

Esto generará posts flash estándar cuando se cargue la página, y los actualizará o replaceá después de cualquier request de Ajax. Si está utilizando la gem react-rails , puede simplemente colocar el file en el directory de components y todo debería funcionar.

Consulte este blogpost que muestra los posts flash de Rails con React, donde describió exactamente lo que pregunta:

enter image description here

En resumen, muestra cómo crear el componente de reacción de FlashMessages y save una reference al mismo:

 window.flash_messages = this; // in FlashMessages constructor 

Por lo tanto, es posible enviar posts por el lado del cliente:

 window.flash_messages.addMessage({ id: 'id', text: 'Hello, fellas!', type: 'success' });