How use react components from npm in rails - ruby-on-rails

I am new in react and I follow this article https://www.airpair.com/reactjs/posts/reactjs-a-guide-for-rails-developers
to use react in rails app and everything was fine. But later I wanted include in records.html page summernote (WYSIWYG html editor). I found this npm module react-summernote and can't make editor works.
This is my code (try 1):
application.js
var ReactSummernote= window.ReactSummernote = global.ReactSummernote = require('react-summernote');
index.html.erb
<%= react_component 'ReactSummernote'%>
This is my code (try 2):
summernote.js.coffee
#RichTextEditor = React.createClass
render: ->
React.createElement ReactSummernote
index.html.erb
<%= react_component 'RichTextEditor'%>
and got this error
react.self-e3251ec….js?body=1:1285 Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of Constructor.

Some things worth checking;
I'm assuming your node packages are being installed to /node_modules/*. Make sure those packages are being added to your asset pipeline. I've used browserify to accomplish this.
Check in your browser console to see if the variable is being set (just type ReactSummernote. The result should be a function if it is imported correctly, or undefined if there is some problem).
If you've gotten this far and are still stuck, I would recommend triggering the render without using react-rails. It's easy enough to render the component onload of your page through vanilla javascript, and if it's the react-rails gem that is causing you problems, this will navigate around it easily with something like this;
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

Related

VS Code Code Navigation inside haml files

I have been playing around with VS Code hoping to switch over from the pricier Jet Brains option. However "you get what you pay for" may hold true here.
I have a rails project that uses haml templates and I have some inline javascript in these haml files. Like so:
- content_for :head do
:javascript
$(document).ready(function () {
initializeCurrencyFormattingOnAllCurrencyElements();
if (typeof table_manager === 'undefined') {
table_manager = new DatatableManager();
}
var sales = new AsyncDatatable('sales', '#sales', 100, {}, false, true, 1);
sales.handle_form_submit();
table_manager.add(sales);
table_manager.init_all();
table_manager.draw_all();
});
In the JetBrains IDE it was no problem ctrl/command hovering over these javascript definitions inside the haml file and I could use code navigation to go straight to the definition of things like "AsyncDatatable", "handle_form_submit()", etc. However I cannot get this to work in VS Code. I struggled for a while to get code navigation working for ruby as well (my installation came with a blank settings.json file for some reason) So I don't know whether I just don't have the right extensions, settings, or whether this just is asking too much from a free IDE.
Has anyone succeeded in doing this either with haml or erb? Any tips would be greatly appreciated even if the answer is that this isn't currently possible.
Personally i have been using VScode for a while and what i do is copy the phrase i want to find e.g. "AsyncDatatable" and then using search (shift+command+F for mac) that shows that phrase even if it appears multiple times inside your code.

How to SSR nested components or slotted content with vueonrails?

I am trying to add Vue.js components to a Ruby-on-Rails app with SSR support by using Hypernova and vueonrails helpers.
I am able to render a registered component with Hypernova from a view (.html.erb) by using this helper:
<%= render_vue_component('HelloWorld', message: 'Hello World from SSR!') %>
Let's say my HelloWorld component has a <slot> in its template:
<template>
<div class="helloworld">
<h1>{{ message }}</div>
<slot></slot>
</div>
</template>
I want to replicate something like the following in the .html.erb using render_vue_component:
<hello-world message="Hello World from SSR!">
<hello-world>SSR Nested component</hello-world>
</hello-world>
How could I pass child elements to the render_vue_component helper so they get rendered by Hypernova?
Well, I analyzed the repo and by looking at the it, I think you should use the render_vue_component only for rendering the root component, like the main.js when you start a project with #vue/cli:
<%= render_vue_component('App', { router }) %>
I came to this conclusion by analyzing the vueonrails source code.
If you look to the render_vue_component function, it only takes the identifier and a data object, which internally calls render_react_component.
By this signature, it simply don't fit with Vue's render function signatures, which are (element, children) or (element, attributes, children).
Maybe you find a better support by opening an issue on the repo.
But I'd think twice before using this project because there isn't any documentation so far and checking the website, seems like they are more focused in selling books about the project than creating a documentation.

React on rails not rendering

I am trying to render my react component on my ruby on rails app. There is no error but the page renders nothing. I suspect if the tutorial is outdated or if failed to asked me to import some stuff.
I followed closely to this site: https://medium.com/quick-code/simple-rails-crud-app-with-react-frontend-using-react-rails-gem-b708b89a9419
This is probably the most critical part of the tutorial.
Let’s return to config/routes.rb for a second and add our Home index page to be the root:
root to: 'home#index'
Now all our index page need is a corresponding view. Create a new ‘home’ folder inside app/views and add index.html.erb file to it:
app/views/home/index.html.erb
This file will only contain one line that takes advantage of react_component view helper that comes with ‘react-rails’ gem. It’s not going to work right now, but it all will make sense in a moment.
<%= react_component 'Main' %>
Creating our first React Component
Create a file named app/assets/javascripts/components/_main.js.jsx
We are going to follow the usual syntax for creating a stateless/presentational React Component that we will call Main:
const Main = (props) => {
return(
<div>
<h1>Fruits are great!</h1>
</div>
)
}
Head to localhost:3000 in the browser and see for yourself that our first Component was loaded on the page. Pretty cool!
I tried to include code like
import React from "react"
import ReactDOM from 'react-dom';
but nothing renders too.
When trying to debug, I tried not to deviate from the tutorial as I am still new and hope to learn from one source at a time to prevent conflicting and different methods.

Using Rails data as the defaultProps in react component

I'm using react-rails (with sprockets rather than webpack, if it turns out to be relevant), trying to make a reusable component for a sprawling rails application. It's basically a navbar, and we have a list of links we usually want to display, but we also need to be able to override that list on some pages.
So far so good. However!
We have the array of links saved as a constant in our rails app, and ideally, the default links would be the defaultProps for the react component. We don't want to duplicate that information in the react component, because the whole point of making this component is that we're trying to move toward consistency, and if we need to make updates hopefully we'll only make them in one place.
So this is an option:
<%= react_component "Navbar", links: navbar_default_links, other_settings: true, etc: false %>
But having to do that on every page seems inelegant when really we just want to call out when the links are different than the usual ones.
Does anyone know a way to pull information from Rails into the definition of the react component using react-rails? Or another solution?
(Edit: to be clear, this doesn't need to be information from the rails database)
Here are the ways I'm aware of to communicate to share constants between Rails and React.
Option #1: Wrap react_component
def render_navbar(props = {}, options = {})
react_component 'Navbar', {links: navbar_default_links}.merge(props), options
end
Option #2: (If you use Webpack) Declare the links in a JSON or YAML that both Rails and your bundler can consume.
# The file that used to define the constant
def navbar_default_links
JSON.load(File.read(Rails.root.join('path/to/your/file.json')))
end
// In `Navbar.js`, using json-loader
import navbarDefaultLinks from './path/to/navbar_default_links.json';
Option #3 (If you use the Asset pipeline) Add .erb to your JS file and use the constant
// Navbar.js.erb
const defaultProps = <%= MyModule::NAVBAR_DEFAULT_LINKS %>
Options #4 Communicate through a global object like window. It breaks encapsulation but hey 🤷‍♂️
In application.html.erb
<script>
window.navbar_default_links = <%= MyModule::NAVBAR_DEFAULT_LINKS %>
</script>
<%= `window.navbar_default_links` must be above this line %>
<%= javascript_pack_tag 'application' %>
// In `navbar.js`
const navbarDefaultLinks = window.navbar_default_links

How to disable Backbone code to call an underscore template in non-essential pages?

I have an app that uses Backbone on the client side and Rails on the back end.
My Backbone code calls an underscore template in this manner:
var todo = Backbone.View.extend({
.
template : _.template( $('#todo_rows').html() ),
.
}
However, there's only one page on the site which would need to call this template, and the Backbone js loads on every page. That means that there will be a JS error on the other pages which don't have the "#todo_rows" underscore template defined.
One solution would be to create a blank "#todo_rows" template section for all the other pages, but this strikes me as a major hack and not the best way to deal with this problem.
What is the best way to disable the code to call a template in Backbone? It needs to be disabled if you are not on the home page OR if you are not logged in.
Underscore's template is happy with receiving an empty string as argument. As $('#todo_rows').html() returns null if the #todo_rows element does not exist, you can easily avoid the JS error by using the or-operator idiom:
template : _.template( $('#todo_rows').html() || '' ),
which means "use an empty string as dummy template if the real one is not available".
Another, Rails-specific workaround for this problem (and a neat approach in general) is to add the ejs gem to your project's Gemfile, which allows you to put each template in a separate .jst.ejs file in app/assets and have the Asset Pipleline compile it to a JST object that can be accessed by your view to get the template content:
templates/todo_rows.jst.ejs
<% // Your template (content of former "#todo_rows") here %>
todo_rows_view.js
//= require templates/todo_rows
var todo = Backbone.View.extend({
template : _.template(JST['templates/todo_rows']),
// ...
}
This avoids the roundtrip to the DOM and makes it possible to nicely organize the templates into multiple files, but has the drawback that the template string will be sent as part of the JavaScript code for every page including it.

Resources