My company is taking its first steps in integrating an existing React SPA into a brand-new Phoenix app. We’ve set up auth using phoenix.gen.auth, and are looking at the first case where we need Phoenix-generated data in our UI layer: adding a logout link to our header.
This is how the link is generated in _user_menu.html.heex
:
<li><%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %></li>
What’s the best way to recreate this in our React app?
The first approach I came up with was to add a template that puts any data we might need into a <script></script>
tag and attaches it to the window
object so that our React components could grab it. This is how I implemented it:
root.html.heex
:
<head>
...
<%= render "_script_data.html", assigns %>
...
</head>
_script_data.html.heex
:
<script type="text/javascript">
window.dashboardAppData = {
currentUser: {
<%= if @current_user do %>
email: '<%= @current_user.email %>',
<% end %>
},
csrfToken: document.querySelector("meta[name='csrf-token']").getAttribute("content"),
routes: {
userSessionDelete: '<%= Routes.user_session_path(@conn, :delete) %>',
...
}
}
</script>
My React component, HeaderToolbar.js
:
function HeaderToolbar() {
const appData = window?.dashboardAppData;
return (
{appData && (
<a
href={appData.routes.userSessionDelete}
data-to={appData.routes.userSessionDelete}
data-method="delete"
data-csrf={appData.csrfToken}
>
Log Out
</a>
}
)
}
This approach seems workable, but far from ideal: I don’t love the step of creating a giant object of data and making decisions each time about naming conventions. I also lose the utility of the <%= link ... %>
tag, as I’m forced to recreate what a Phoenix link looks like, mirroring the necessary data attributes first by rendering it normally in a template and then recreating it in my React component.
Does anyone have any suggestions for better ways to approach this integration? Whatever choices we make at this point will determine the pattern for how we do quite a bit of other work and I’d like to find the best option possible.