PhoenixLiveReact

repository·master·Indexed 42 days ago

https://github.com/fidr/phoenix_live_react

A library and hook for rendering React.js components directly within Phoenix LiveView views. It bridges Phoenix's real-time Elixir capabilities with React's component-based UI model, providing the live_react_component/3 helper and built-in props like pushEvent, pushEventTo, and handleEvent for bidirectional communication between React and LiveView.

Tokens
3.4K
Snippets
13
Records
13
Agent score
55%

What's inside PhoenixLiveReact

  1. Register React components for PhoenixLiveReact

    master

    For phoenix_live_react to find your components, you must attach them to the global window.Components object in your JavaScript assets (e.g., app.js).

    Example:

    import { MyComponent } from "./components/MyComponent"
    
    window.Components = {
      MyComponent
    }
  2. Configure Phoenix 1.5 or older with React

    master

    For older Phoenix versions, you need to manually install React and configure Babel to handle JSX.

    1. Install Dependencies

    In your assets directory, run:

    npm install react react-dom --save
    npm install @babel/preset-env @babel/preset-react --save-dev

    2. Configure Babel

    Create an assets/.babelrc file:

    {
      "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
      ]
    }

    3. Webpack Alias (NPM users only)

    If using Webpack, add the following to assets/webpack.config.js to ensure React is resolved correctly:

    module.exports = (env, options) => ({
      resolve: {
        alias: {
          react: path.resolve(__dirname, './node_modules/react'),
          'react-dom': path.resolve(__dirname, './node_modules/react-dom')
        }
      }
    });
  3. Install PhoenixLiveReact

    master

    To use phoenix_live_react, you must add it to your Elixir dependencies and connect the JavaScript hooks to your LiveView socket.

    1. Add Elixir Dependency

    Add {:phoenix_live_react, "~> 0.5"} to your mix.exs file and run mix deps.get.

    2. Connect JavaScript Hooks

    In your app.js, import LiveReact and initLiveReact, then include LiveReact in your LiveSocket hooks configuration. You can optionally call initLiveReact() on DOMContentLoaded to speed up initial rendering (note: pushEvent, pushEventTo, and handleEvent props are not available during this initial client-side render).

    3. Import Helper in Elixir

    Import PhoenixLiveReact in your web module (e.g., lib/MyAppWeb.ex) within the live_view quote block to make the component helpers available to your views.

    # mix.exs
    def deps do
      [
        {:phoenix_live_react, "~> 0.5"}
      ]
    end
    // app.js
    import LiveReact, { initLiveReact } from "phoenix_live_react"
    
    let hooks = { LiveReact }
    
    let liveSocket = new LiveSocket("/live", Socket, { hooks, params: { _csrf_token: csrfToken } })
    
    // Optional: render on page load for speed
    document.addEventListener("DOMContentLoaded", e => {
      initLiveReact()
    })
    # lib/MyAppWeb.ex
    def live_view do
      quote do
        import PhoenixLiveReact
      end
    end
  4. Configure Phoenix 1.6 with NPM and React

    master

    If you are using Phoenix 1.6, follow these steps to set up NPM and React in your assets directory:

    1. Initialize NPM

    In your assets directory, run:

    npm init

    2. Update config.exs

    Update your NODE_PATH in config/config.exs to include both your project's deps and the assets/node_modules directory so esbuild can find the packages.

    env: %{"NODE_PATH" => Enum.join([Path.expand("../deps", __DIR__), Path.expand("../assets/node_modules", __DIR__)], ":")}

    3. Install React

    In your assets directory, run:

    npm add react react-dom
    npm init
    npm add react react-dom
  5. Use the default LiveReact hook in Phoenix LiveView

    master

    The library exports a default hook object (represented as y in the source) designed to be used within a Phoenix LiveView Hook. When this hook is used, it manages the React lifecycle (mounting, updating, and unmounting) automatically.

    Hook Lifecycle Methods:

    • mounted(): Initializes the React component. It reads the component class from data-live-react-class and props from data-live-react-props. If data-live-react-merge is present, it merges the data attributes with the provided options.
    • updated(): Re-renders the React component when the LiveView element is updated.
    • destroyed(): Unmounts the React root to prevent memory leaks.
    // Example of using the default hook in your app.js
    let LiveReactHook = {
      mounted() {
        // The default hook logic is applied here
        // It uses data-attributes on this.el to find the component and props
      },
      updated() {
        // Re-renders component on LiveView update
      },
      destroyed() {
        // Unmounts React component
      }
    };
    
    let callbacks = {
      mounted: LiveReactHook.mounted,
      updated: LiveReactHook.updated,
      destroyed: LiveReactHook.destroyed
    };
    
    let Socket = require("phoenix").Socket;
    let liveSocket = new Socket("/socket", {params: {token: window.token}});
    
    liveSocket.connect()
      .then(() => liveSocket.join("room:lobby"))
      .then(() => {
        let liveSocket = new LiveSocket("/live", Socket, {params: {}});
        liveSocket.connect({params: {}}).then(() => {
          // Register the hook
          Phoenix.LiveSocket.register("LiveReact", callbacks);
        });
      });
  6. Push events from React to LiveView

    master

    PhoenixLiveReact automatically passes LiveView event functions as props to your React component. You can use these to communicate from the client-side React code back to the Elixir LiveView.

    Available Props:

    • pushEvent(event, payload, (reply, ref) => ...): Pushes an event from the client to the LiveView.
    • pushEventTo(selector, event, payload, (reply, ref) => ...): Pushes an event from the client to a specific LiveView component (using a CSS selector).
    • handleEvent(event, handler): (For phoenix_live_view >= 0.14) Allows receiving data directly through LiveView push_event.

    Example Usage in React:

    const { pushEvent, pushEventTo, handleEvent } = this.props;
    
    // Push a simple event
    pushEvent("button_click");
    
    // Push an event with a payload
    pushEvent("myevent", {"var": "value"});
    
    // Push to a specific component via selector
    pushEventTo("#component-1", "do_something");
    
    // Handle incoming events
    handleEvent("some-event", (payload) => console.log(payload));
    const { pushEvent, pushEventTo, handleEvent } = this.props;
    pushEvent("button_click");
    pushEvent("myevent", {"var": "value"});
    pushEventTo("#component-1", "do_something")
    
    handleEvent("some-event", (payload) => console.log(payload))
  7. Use live_react_component in LiveViews

    master

    Once installed and configured, use the live_react_component/3 helper in your .heex templates to render a React component. The first argument is the component name as a string (prefixed by Components.), the second is the props list, and the third is the id.

    <%= live_react_component("Components.MyComponent", [name: @name], id: "my-component-1") %>
  8. Handle events between React and LiveView

    master

    Phoenix LiveView automatically passes three event functions as props to your React components. These allow the React component to communicate back to the server.

    Available Props

    • pushEvent(event, payload, (reply, ref) => ...): Pushes an event from the client to the LiveView.
    • pushEventTo(selector, event, payload, (reply, ref) => ...): Pushes an event from the client to a specific LiveView component (using a CSS selector).
    • handleEvent(event, handler): (Requires phoenix_live_view >= 0.14) Allows receiving data directly through LiveView's push_event mechanism.

    Usage Example

    const { pushEvent, pushEventTo, handleEvent } = this.props;
    
    // Push to the current LiveView
    pushEvent("button_click");
    pushEvent("myevent", {"var": "value"});
    
    // Push to a specific component via selector
    pushEventTo("#component-1", "do_something");
    
    // Listen for events from the server
    handleEvent("some-event", (payload) => console.log(payload));
    const { pushEvent, pushEventTo, handleEvent } = this.props;
    
    pushEvent("button_click");
    pushEvent("myevent", {"var": "value"});
    pushEventTo("#component-1", "do_something")
    
    handleEvent("some-event", (payload) => console.log(payload))
  9. Render a React component in LiveView with live_react_component/3

    master

    Use PhoenixLiveReact.live_react_component/3 to render a React component within a Phoenix LiveView template. This function generates the necessary HTML elements (a receiver and a container) to bridge the React component with the LiveView lifecycle.

    Arguments:

    • name: A String representing the module name of the React component.
    • props: A Map or keyword list containing the props to be passed to the React component. These are serialized to JSON and passed via data-live-react-props.
    • options: A keyword list for rendering configuration (see Configure component rendering options).
    <%= PhoenixLiveReact.live_react_component("Components.MyComponent", %{name: "Bob"}, id: "my-component-1") %>
  10. Initialize PhoenixLiveReact with initLiveReact()

    master

    Use initLiveReact() to automatically scan the DOM for all elements containing the data-live-react-class attribute and initialize them as React components. This is useful for bulk-initializing components on page load.

    // Call this to find all elements with [data-live-react-class] and mount them
    phoenix_live_react.initLiveReact();
  11. Initialize a specific React element with initLiveReactElement()

    master

    Use initLiveReactElement(el, options) to manually initialize a specific DOM element as a React component. This is typically used inside a Phoenix LiveView hook's mounted() lifecycle callback.

    el is the DOM element containing the data-live-react-class attribute. options is an object containing props to be passed to the React component (e.g., pushEvent, pushEventTo, handleEvent).

    // Inside a Phoenix LiveView hook
    initLiveReactElement(this.el, {
      pushEvent: this.pushEvent.bind(this),
      pushEventTo: this.pushEventTo.bind(this),
      handleEvent: this.handleEvent.bind(this)
    });
  12. Configure component rendering options

    master

    When calling live_react_component/3, you can pass several options in the third argument to customize the HTML output:

    • id: Sets the base ID for both the receiver and the container (the container will automatically append -container to this ID).
    • container: A keyword list of attributes for the container div.
    • container_tag: The HTML tag to use for the container (defaults to :div).
    • receiver: A keyword list of attributes for the receiver element.
    • receiver_tag: The HTML tag to use for the receiver (defaults to :div).
    • binding_prefix: Overrides the default LiveView binding prefix (defaults to "phx-").
    • merge_props: A boolean. If true, sets data-live-react-merge to true.
    <%=
      PhoenixLiveReact.live_react_component("Components.MyComponent", %{},
        id: "my-component-1",
        container: [class: "my-component"],
        container_tag: :p
      )
    %>