Reactive WebSockets with Phoenix Channels
@MichalZalecki
michalzalecki.com
woumedia.com
reactive
\rē-ˈak-tiv\
done in response to a problem or situation
‒ merriam-webster.com
Library doesn't matter
- RxJS 4
- RxJS 5
- most.js
- xstream
- Kefir
- Bacon.js
- whatever...
RxJS 5 (beta)
Rx.Observable.fromEvent(channel, "counter_changed")
most.js
most.fromEvent("counter_change", channel)
// Uncaught Error: source must support
// addEventListener/removeEventListener
// or addListener/removeListener
most.js
import * as most from "most";
import mostCreate from "@most/create";
mostCreate(add => channel.on("counter_changed", add));
Phoenix Channels
defmodule PhoenixChannels.RoomChannel do
use Phoenix.Channel
def join("rooms:lobby", _message, socket) do
{:ok, socket}
end
def join("rooms:" <> _private_room_id, _params, _socket) do
{:error, %{reason: "unauthorized"}}
end
def handle_in("new_msg", %{"body" => body}, socket) do
broadcast! socket, "new_msg", %{body: body}
{:noreply, socket}
end
def handle_out("new_msg", payload, socket) do
push socket, "new_msg", payload
{:noreply, socket}
end
end
Non-reactive way
const channel = socket.channel("rooms:lobby", {});
const chatInput = document.querySelector("#chat-input");
const messagesContainer = document.querySelector("#messages");
chatInput.addEventListener("keypress", event => {
if (event.code === "Enter") {
channel.push("new_msg", { body: event.target.value });
chatInput.value = "";
}
});
channel.on("new_msg", payload => {
const li = document.createElement("li");
li.textContent = payload.body;
messagesContainer.appendChild(li);
});
Reactive way
const channel = socket.channel("rooms:lobby", {});
const chatInput = document.querySelector("#chat-input");
const messagesContainer = document.querySelector("#messages");
const newMessage$ = Rx.Observable.fromEvent(chatInput, "change")
.map(e => e.target.value);
newMessage$.subscribe(body => channel.push("new_msg", { body }));
newMessage$.subscribe(() => chatInput.value = "");
Rx.Observable.fromEvent(channel, "new_msg")
.pluck("body")
.scan((akk, msg) => `${akk}<li>${msg}</li>`, "")
.subscribe(msgs => messagesContainer.innerHTML = msgs);
deck
By Michał Załęcki
deck
- 1,318