Phoenix-js-react-hooks - React Hooks for working with Phoenix Channels in JavaScript

I built a silly site for Halloween that uses Phoenix Channels on the backend, and React on the frontend. I had many problems integrating the official phoenix NPM module with my React components. After many iterations I’ve got something that fits in naturally with React function components, so I’ve extracted it as a library to share, and published it on NPM and GitLab.

Here’s part of the README:

phoenix-js-react-hooks

React hooks for working with Phoenix’s channels.

Usage

Wrap the top of your app with a <SocketProvider> so child components will have
access to the socket.

import { SocketProvider } from '@ericlathrop/phoenix-js-react-hooks';

function App() {
  const socketUrl = "wss://localhost:4000/socket";
  const socketOptions = { params: { 'user_id': userId }};

  return (
    <SocketProvider url={socketUrl} options={socketOptions}>
      <Main />
    </SocketProvider>
  );
}

The useChannel hook lets you subscribe to a channel, and the useEventHandler
hook lets you respond to incoming messages:

import React, { useContext, useEffect, useRef, useState } from 'react';
import { sendMessage, useChannel, useEventHandler } from '@ericlathrop/phoenix-js-react-hooks';

export function Main() {
  const [messages, setMessages] = useState([]);

  const room = 'cute kitties';
  const { channel: chatChannel } = useChannel('chat:' + room, undefined, (channel, { messages: initialMessages}) => {
    setMessages(initialMessages);
  });

  useEventHandler(chatChannel, 'new_message', message => {
    setMessages(messages.slice(0).concat([message]));
  });

  return (
    <div className="chat-messages" >
      {messages.map(({ id, message }) => <ChatMessage key={id} message={message} />)}
    </div>
  );
}
6 Likes

It worked. Thanks.

@ericlathrop Just out of curiosity

export function useEventHandler(channel, event, handler) {
  const handlerFun = useRef(handler);
  handlerFun.current = handler;

Is there a reason for the 3rd line? Is this to dynamically change the handler? If so what are the use cases for this?

Correct. I was passing in a lambda so the handler would be different every time the component renders, so the reference needs to get updated.

For example:

  useEventHandler(chatChannel, 'new_message', message => {
    setMessages(messages.slice(0).concat([message]));
    messageWindow.current.scrollTop = messageWindow.current.scrollHeight;
  });

If the reference handler reference never got updated, then the first handler would stay with messages being [] and only the last message would get added for each event.

2 Likes