npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

use-phoenix

v0.0.2-alpha.5

Published

React hooks for the Phoenix Framework

Downloads

6

Readme

use-phoenix

The library will not follow semver until version 1.0.0. expect potential breaking changes on any new versions until then.

Connecting to your Phoenix Socket

Wrap the intended part of your application with a PhoenixProvider.

// App.tsx
import { PhoenixProvider } from 'use-phoenix';

const Application = () => {
  return <PhoenixProvider>...</PhoenixProvider>;
};

Passing a url and params to your PhoenixProvder will connect to your socket instantly on mount:

return (
  <PhoenixProvider
    url="ws://localhost:4000/socket"
    options={{
      params: { token: '123' }
    }}
  >
    ...
  </PhoenixProvider>
);

You could instead use the usePhoenix hook to connect lazily using connect:

To use this option do not pass a url into PhoenixProvider:

// App.tsx
return <PhoenixProvider>...</PhoenixProvider>;

Later on when you would like to connect the socket:

// Component.tsx
import { usePhoenix } from 'use-phoenix';

const Component = () => {
  const { socket, connect } = usePhoenix();

  useEffect(() => {
    connect('ws://localhost:4000/socket', {
      params: { token: 'xyz' }
    });
  }, [connect]);
};

usePhoenix also provides isConnected which becomes true when the socket has successfully connected.

Quick Start - useEvent & useChannel

You can pass a short circuit expression to delay connection to an event or channel. If for example you are waiting to recieve an id to use from some network request, useEvent and useChannel will not connect until it is defined. Below is a contrived example:

interface PingEvent {
  event: 'ping',
  data: {
    body: string;
  }
}

interface PongEvent {
  event: 'pong';
  data: {
    message: string;
  };
}

interface JoinPayload {
  secret: string;
}

interface PingResponse {
  ok: boolean;
}

  // Channel will not connect until id is defined
  const [channel, { push, data }] = useChannel<JoinPayload>(id && `chat:${id}`);
  //                      ^^^^
  //        data is typed according to `JoinPayload`

  // Events will not be listened to until data.secret is defined
  const { data } = useEvent<PongEvent>(channel, data?.secret && `pong:${data.secret}`);

  const handleClick = () => {
    const { ok } = await push<PingEvent, PingResponse>('ping', { body: 'Hello World' })
    //      ^^ Typed according to PingResponse
  }

  return (
    <div>
      <button onClick={handleClick}>
        ping
      </button>
      <p>{data && data.message}</p>
    </div>
  );

useEvent

useEvent is a hook that allows you to succinctly listen in on channel events.

Example Usage

...
// Type check your event
type joinEvent = {
	event: 'join',
	data: {
		members: Record<string, User>
	}
}

const [channel, { isSuccess, isError, ...rest }] = useChannel('chat:lobby')

// pass in a channel directly
const { data } = useEvent<JoinEvent>(channel, 'join')

// typed
console.log(data.members)

Optionally, if you would rather capture the response in a callback you can (or both):

const { data } = useEvent<JoinEvent>(channel, 'join', (data) => {
  console.log(response);
});

useChannel

useChannel gives you important functions and information about the state of the channel. The following properties are available for useChannel

data: JoinPayload | null; // the join response from the server
status: ChannelStatus;
isSuccess: boolean;
isLoading: boolean;
isError: boolean;
error: any;
push: PushFunction // push a message to the server
leave: () => void // leave the channel

Example Usage

const [channel, { push }] = useChannel('room:1');

push('new_msg', { msg: 'Hello World' });
leave();

Waiting for another useChannel to make the connection

Consider the case where you are using useChannel in multiple components, but only one of the components really has the necessary params to connect to the channel topic, and you just want the other components to work with the channel after the channel has been connected. The problem is, if the component that does not have access to connection params occurs earlier in the react tree, it will naturally try to connect and be unable to because the required params are contained within a later component in the tree.

What you can do is indicate useChannel to yield and wait for another useChannel to connect and once it connects, the yielded useChannel will connect to the instance and operate as usual. Note if no connection is ever made, a yielded useChannel will never connect.

Example

// map.tsx component with access to important coordinates
const [channel] = useChannel('map:1', { params: { coordinates: [0, 0] }});
//                                                             ^^^^^
//                                                   value only known by main.tsx

// Layout.tsx which doesnt have access to the coordinates...
const [channel] = useChannel('map:1', { yield: true });
//                                      ^^^^^
// this channel will wait until map.tsx connects instead of connecting itself.
// thus allowing you to not need params

Leaving a channel

useChannel does not automatically leave the channel when the hook unmounts. That is, the socket will continue to listen in on the channel. It is best to handle leaving the channel explicitly using leave if you would like to leave the channel on component unmounts:

const [channel, { leave }] = useChannel('chat:lobby');

useEffect(() => {
  return () => {
    leave();
  };
}, [leave]);

usePresence

Quickly setup presence tracking by connecting to your presence channel

const users = usePresence('room:lobby');

the response is transformed to make it easier to use. Typically it is an object of id: { ..., metas: [{ ... }] }. In all of my use cases, the metas field is always an array of one object, and I found myself having to constantly drill into the first index metas[0]. Thus, the hook automatically returns metas[0] if the metas field is a single index array.

The response takes the form:

[
	{
		id: number;
		metas: object;
		// any other fields from your server.
	}
]

Say you add a user field to each presence from your server; you can easily type the field using generics

const users = usePresence<{ user: User }>('room:lobby');

// typed User
users[0].user;

// id and metas are automatically typed
users[0].id;

Additionally, you can type extra fields into the metas:

// If you have a custom metas, type it easily
const users = usePresence<void, { lastSeen: string }>('room:lobby');

// typed lastSeen
users[0].metas.lastSeen;