Passing JSON between simple ReactJS and Phoenix/Elixir

Doing some really simple react rendering, no redux, no react-router, just a ReactDOM.render on a div id inside the eex template. I’ve figured out how to pass information from react to phoenix like so:

handleSubmit = event => {
    event.preventDefault()

    var request = new XMLHttpRequest();
    request.open('POST', '/sessions/sign_in', true);
    request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
    request.send(JSON.stringify({ email: this.state.email, password: this.state.password }));
}

I am having trouble passing information from phoenix to react though.
I am passing a list of categories through the controller to a template like so:

<%= for {:ok, categories} <- @categories do %>
  <script>
    var CATEGORIES = <%= Jason.encode(categories) %>
  </script>
<% end %>

<div id="react-categories"></div>

how do I consume this variable within my react app? Is there a better way to be passing this around?

my component looks like:

import * as React from 'react';
import "./categories.css"

class Categories extends React.Component {
    constructor() {
        super();

        this.state = {
            showMenu: false,
        }
        this.showMenu = this.showMenu.bind(this);
    }

    showMenu(event) {
        event.preventDefault();

        this.setState({
            showMenu: !this.state.showMenu,
        });
    }

    render() {
        return (

            <div className="menu">
                <button className="menu-button" onClick={this.showMenu}>
                    Menu
                </button>
                {
                    this.state.showMenu
                        ? (
                            <div>
                                <ul className="menu-items">
                                    <li className="menu-item"><button className="menu-item-button"> Menu item 1 </button></li>
                                    <li className="menu-item"><button className="menu-item-button"> Menu item 2 </button></li>
                                </ul>
                            </div>

                        )
                        : (
                            null
                        )
                }
            </div>
        )
    }
}

export default Categories;

You probably need raw infront of your Jason.encode(view source to confirm but it will probably be escaped currently).

Then in your app.js or whereever you call reactdom.render you can just pass it in as props

ReactDOM.render(<YourComponent categories={CATEGORIES}/>, document.getElementById('react-categories'))

4 Likes

I was kind of confused why the values were being escaped, thanks for that.
Please forgive me as I’m still learning React but, how would I call that variable within the component?

It will be available in this.props.categories inside the component.

2 Likes

I like to use https://github.com/geolessel/react-phoenix as it makes it very easy to pass props from your Phoenix views into react components without having to worry about the serialization yourself.

2 Likes

Old school, without polluting the global namespce:

<!DOCTYPE html>
<html>
  <!-- dist/index.html -->
  <head>
    <title>Minimal React</title>
  </head>
  <body>
    <div id="app"></div>
    <script id="appProps" type="application/json">
     {
       "categories": ["1","2","3"]
     }
    </script>
    <script src="./bundle.js"></script>
  </body>
</html>
// src/index.js
//
import React from 'react'
import ReactDOM from 'react-dom'

const data = document.getElementById('appProps')
const props = JSON.parse(data.textContent)

ReactDOM.render(
    <Categories {...props} />,
    document.getElementById('app')
)

function Categories({categories}) {
  const toItem = (category) => <li key={category}>{category}</li>
  const list = categories.map(toItem)
  return <ul>{list}</ul>
}
3 Likes

I like that a lot thanks for sharing.

1 Like