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

phoenix-react

v0.1.0

Published

A React context provider to handle Phoenix channels websocket events

Downloads

6

Readme

phoenix-react

The package offers state support for Phoenix Channels using the React's Context API. The only external dependency is Phoenix js.

It provides :

  • Central state for your events
  • Simple channel and events registration
  • HOC for easy coupling with the Provider

Running the example

  • Clone the repository
git clone https://github.com/nicklayb/phoenix-react
  • Install npm packages
npm install
//  or
yarn install
  • Run development environment
npm run dev

Getting started

Install the dependency

Using npm

npm install --save phoenix-react

Using yarn

yarn add phoenix-react

Setting up the context Provider

import React from 'react'
import ChannelProvider from 'phoenix-react'
import channels from './channels'

//  Refers the initial state you want
const state = {
  messages: [],
}

//  Refering to the `opts` parameter for Pheonix.Socket instanciation
const params = {
  params: {
    token: localStorage.token,
  },
}

export default () => (
  <ChannelProvider
    url="ws://localhost:4000/socket"
    channels={channels}
    state={state}
    params={params}
  >
    <App />
  </ChannelProvider>
)

Writings channels handler

This channels prop of the provider is an array of channel you create with the createChannel helper. The first parameter is the topic to subscribe and the second one is a callback that will setup the socket.

Within the callback function, you receive the channel as first paramter and the provider value which has mutate, fire, state and gettersprops. I highly recommend creating mutats

import { createChannel } from 'phoenix-react'

//  Messages mutators. These are helpers that will mutate the state withing a payload. Even though these are not required, they are recommended.
const messageMutators = {
  add: (state, message) => ({
    ...state,
    messages: [
      ...state.messages,
      message
    ],
  }),
  set: (state, messages) => ({
    ...state,
    messages: messages,
  }),
}

//  The message channel, the mutate function performs a state mutation of the provider which will re-render the consumer components.
const messageChannel = createChannel('room', (channel, { mutate }) => {

  //  You can add handlers for different events. This one with add a message to the state when the `new_msg` event is broadcasted by the Phoenix server
  channel.on('new_msg', ({ body }) => {
    mutate(state => messageMutators.add(state, body.message))
  })

  //  Don't forget to join the channel. You can also chain `receive(message, body)` to it to handle different server return code. See Phoenix.js documentation for more information
  channel.join()
    .receive('ok', ({ body }) => {
      mutate(state => messageMutators.set(state, body.messages))
    })

  //  Return that channels to it can be stored into the provider
  return channel
})

export default [
  messageChannel,
]

Coupling components to the provider

To bind state mutations to components, there is two way. Wither use the Consumer or the HOC. In either way, you will receive the following payload:

  • fire(channel, event, body): function that fires an event to the server on the desired channel.
  • state: The current state of your provider which should not be mutated. Use the mutate function instead.
  • getters: The getter object you passed to the provider. These are "quick access functions" to your state.
  • mutate(state => state): function that directly mutates the state.
  • leave(topice): function that leaves the given channel.

Using the Consumer

Simply wrap the location where you want to use the provider state by the consumer.

import React from 'react'
import ChannelsProvider from 'phoenix-react'

export default () => (
  <div className="app">
    <div className="message-list">
      <ul>
        <ChannelsProvider.Consumer>
          {({ state }) => state.messages.map(message => (
            <li>[{message.author}]: {message.text}</li>
          ))}
        </ChannelsProvider.Consumer>
      </ul>
    </div>
  </div>
)

Using the High-order component (HOC)

Just wrap the Component while edefault exporting it. The advantage of the HOC is that you can pass a mapState function as second parameter to map the only thing want from the payload.

import React from 'react'
import { withChannels } from 'phoenix-react'

const MessageList = ({ messages }) => (
  <ul>
    {messages.map(message => (
      <li>[{message.author}]: {message.text}</li>
    ))}
  </ul>
)

const WrappedMessageList = withChannels(MessageList, ({ state }) => ({
  messages: state.messages
}))

export default () => (
  <div className="app">
    <div className="message-list">
      <WrappedMessageList />
    </div>
  </div>
)

API

ChannelsProvider

state

object that contains the initial state of the context

{
  messages: [],
}

getters

object that contains fucntion that receives params and state

{
  unread: (_, state) => state.messages.filter(message => !message.read),
}

callbacks

object that has onSocketError and onSocketClose keys for callbacks on socket events. These events will receive the provider value.

{
  onSocketError: (error, provider) => { /* */ }
  onSocketClose: (provider) => { /* */ }
}

url

string that represents the websocket/longpolling host like ws://localohst:4000/socket

params

object that is passed to the socket constructor. There is a nested params key that represents the params sent to the websocket server. Refer to the Phoenix js documentation.

{
  params: {
    token: 'Bearer ...',
  },
}

channels

array of channel created within the createChannel helper.

[
  createChannel('room:lobby', (channel, { mutate }) => {
    /* setup channel */
  }),
  createChannel('room:1', (channel, { mutate }) => {
    /* setup channel */
  }),
]

timeout

number that represents the timeout duration in milliseconds like 5000

withChannels

Component

React component that you want to map the provider value.

export default withChannels(Component)

The component now receives state, fire, getters, mutate and leave.

mapState

function that receive the provider value and return only what you want to attach to your component

export default withChannels(Component, ({ state, fire }) => ({
  messages: state.messages,
  fire,
}))

The component now receives on messages and fire.

createChannel(topic, callback: (channel, providerValue))

function that receives a topic string and a callback. That callback receives channel which is the socket.channel instance and the provider value. The callback must return the channel and make sure to call join before returning.

const channel = createChannel('room:lobby', (channel, { mutate }) => {
  channel.on('new_msg', ({ message }) = {
    mutate(state => {
      ...state,
      messages: [
        ...state,
        message
      ],
    })
  })
  channel.join()
  return channel
})