gwalshington
gwalshington

Reputation: 1495

How safely to pass params to react component with react-rails

I'm using react-rails to add some react componenets to an existing ruby on rails app. I just realized that all the props being passed initially to the component are easily seen if you inspect the component

<%= react_component('ProfileWeeklyWriting', {
    languages: @user.languages,
    currentUser: @current_user,
    responses: @writing_responses,
    clapLottie: asset_url('lottie/clap.json'),
    clapIcon: asset_url('icons/clap.svg'),
    arrowIcon: asset_url('icons/arrow_green.png')
    }) %>

But when you inspect the element, allll those variables are shown!

enter image description here

I know I can just do an ajax call from within the component, but is there a way to pass variables to the component initially, without them being shown to the world?

Upvotes: 0

Views: 1811

Answers (1)

Milos Mosovsky
Milos Mosovsky

Reputation: 2983

Let's take a bit theory about how it works. When you do classic SPA without any backend engine you usually do something like

ReactDOM.render(<App />, document.getElementByID('root'))

Which simply render the root component on place of <div id="root" />. When you apply Rails templates on the top, you are connecting 2 different worlds. ReactDOM and Rails slim templating engine (probably) but they don't know nothing about each other. ReactRails is really simply routine which does something like this:

  • 1 Inject custom react-rails script to page
  • Wait for DOM ready
  • Collect all [data-react-class] elements
  • Iterate through of them and call ReactDOM with props.

You can think of it like calling several "mini apps" but in fact those are only components (react doesn't distinguish app/component, it's always just a component)

So the code is something like this (I didn't check the original code but I wrote own react-rails implementation for my company)

  function init () {
    const elements = document.querySelectorAll('[data-react-class]')

    if (elements.length > 0) {
      Array.prototype.forEach.call(elements, node => {
        if (node) {
          mount(node)
        }
      })
    }
  }

  function mount(node) {
    const { reactClass, reactProps } = node.dataset 
    const props = JSON.parse(reactProps || '{}')
    const child = React.createElement(window[reactClass], props)
    ReactDOM.render(child, node)
  }

Then the DOM ready

document.addEventListener('DOMContentLoaded', e => {
  init()
})

Son in fact Rails doesn't know anything about React, and React doesn't know anything about Rails unless it's not living on window. (THIS METHOD IS HIGHLY DISCOURAGED.

In real world there are ways how to make "server" rendering, which means that this piece of code is done on server to not expose props and whole React lifecycle and just flush real prepared HTML to DOM. That means that in the lifecycle BEFORE HTML is sent to the client, there is called transpiler which compiles those components, you can read about it here

  1. https://github.com/reactjs/react-rails#server-side-rendering

So it just calls those methods with a help of https://github.com/rails/execjs

So the only way how to "not expose" props to the client is to "pre-render" components on backend by some engine (either some JS implementation for your language or directly node.js backend). I hope I gave you a better picture how it works and how it can be solved!

Upvotes: 1

Related Questions