diff --git a/.eslintrc b/.eslintrc
index bf0e755..204574b 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,41 +1,34 @@
{
- "parser": "@typescript-eslint/parser",
- "plugins": [
- "@typescript-eslint",
- "react-hooks",
- "simple-import-sort"
- ],
- "extends": [
- "eslint:recommended",
- "plugin:react/recommended",
- "plugin:react-hooks/recommended",
- "plugin:@typescript-eslint/recommended",
- "prettier"
- ],
- "parserOptions": {
- "ecmaVersion": 2020,
- "sourceType": "module",
- "ecmaFeatures": {
- "jsx": true
- }
- },
- "rules": {},
- "overrides": [
- {
- "files": [
- "*.test.ts",
- "*.test.tsx"
- ],
- "rules": {
- // Allow testing runtime errors to suppress TS errors
- "@typescript-eslint/ban-ts-comment": "off"
- }
- }
- ],
- "settings": {
- "react": {
- "pragma": "React",
- "version": "detect"
- }
- }
+ "parser": "@typescript-eslint/parser",
+ "plugins": ["@typescript-eslint", "react-hooks", "simple-import-sort"],
+ "extends": [
+ "eslint:recommended",
+ "plugin:react/recommended",
+ "plugin:react-hooks/recommended",
+ "plugin:@typescript-eslint/recommended",
+ "prettier"
+ ],
+ "parserOptions": {
+ "ecmaVersion": 2020,
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ },
+ "rules": {},
+ "overrides": [
+ {
+ "files": ["*.test.ts", "*.test.tsx"],
+ "rules": {
+ // Allow testing runtime errors to suppress TS errors
+ "@typescript-eslint/ban-ts-comment": "off"
+ }
+ }
+ ],
+ "settings": {
+ "react": {
+ "pragma": "React",
+ "version": "detect"
+ }
+ }
}
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
index 2e0033a..ef8e9b5 100644
--- a/.prettierrc.cjs
+++ b/.prettierrc.cjs
@@ -2,12 +2,12 @@
* {@type require('prettier').Config}
*/
module.exports = {
- useTabs: false,
- printWidth: 100,
- singleQuote: true,
- trailingComma: 'none',
- bracketSameLine: false,
- semi: true,
- tabWidth: 2,
- quoteProps: 'consistent'
+ useTabs: false,
+ printWidth: 100,
+ singleQuote: true,
+ trailingComma: 'none',
+ bracketSameLine: false,
+ semi: true,
+ tabWidth: 2,
+ quoteProps: 'consistent'
};
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84b2ebf..6427ccb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,27 +3,34 @@
2023-12-27
### Breaking changes
+
None
### Additional changes
-* Fix buggy behavior where the reference to `useChannel` functions would be changing on every render. This would cause your useEffects to run even if there should be no change.
+
+- Fix buggy behavior where the reference to `useChannel` functions would be changing on every render. This would cause your useEffects to run even if there should be no change.
# 0.0.1-alpha.6
2023-12-23
### Breaking changes
+
None
### Additional changes
-* Fixed a bug where if you successfully connected to a channel, but then later on the topic supplied to `useChannel` had changed to `null` and then back to the valid topic, the `useChannel` hook functions like `push` would no longer be holding a valid reference to the channel. Now, the hook will successfully update the reference and the functions will work as if the channel topic never changed.
-* Use the internel channel `ref` when using `useChannel`'s `leave`
+
+- Fixed a bug where if you successfully connected to a channel, but then later on the topic supplied to `useChannel` had changed to `null` and then back to the valid topic, the `useChannel` hook functions like `push` would no longer be holding a valid reference to the channel. Now, the hook will successfully update the reference and the functions will work as if the channel topic never changed.
+- Use the internel channel `ref` when using `useChannel`'s `leave`
+
# 0.0.1-alpha.5
2023-12-17
### Breaking changes
-* The typescript type for `useChannel`'s `PushEvent` now aligns with the rest of the types
+
+- The typescript type for `useChannel`'s `PushEvent` now aligns with the rest of the types
+
```jsx
type PushEvent = {
- type: string;
@@ -35,12 +42,14 @@ type PushEvent = {
```
### Additional changes
-* Added rollup build tooling which should reduce bundle size slightly
-* Phoenix.js is now marked as a peer dependency
-* `useChannel` can now accept a short circuit operation to delay connecting to the channel until the condition is met.
-
- ```jsx
- // Delay connecting until id is defined
- const [channel] = useChannel(id && `room:${id}`)
- ```
-* The `push` function type has been improved to catch more potential errors.
+
+- Added rollup build tooling which should reduce bundle size slightly
+- Phoenix.js is now marked as a peer dependency
+- `useChannel` can now accept a short circuit operation to delay connecting to the channel until the condition is met.
+
+ ```jsx
+ // Delay connecting until id is defined
+ const [channel] = useChannel(id && `room:${id}`);
+ ```
+
+- The `push` function type has been improved to catch more potential errors.
diff --git a/README.md b/README.md
index 573602b..671d1bb 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Wrap the intended part of your application with a `PhoenixProvider`.
import { PhoenixProvider } from 'use-phoenix';
const Application = () => {
- return ...;
+ return ...;
};
```
@@ -17,14 +17,14 @@ Passing a `url` and params to your `PhoenixProvder` will connect to your socket
```tsx
return (
-
- ...
-
+
+ ...
+
);
```
@@ -44,13 +44,13 @@ Later on when you would like to connect the socket:
import { usePhoenix } from 'use-phoenix';
const Component = () => {
- const { socket, connect } = usePhoenix();
+ const { socket, connect } = usePhoenix();
- useEffect(() => {
- connect('ws://localhost:4000/socket', {
- params: { token: 'xyz' }
- });
- }, [connect]);
+ useEffect(() => {
+ connect('ws://localhost:4000/socket', {
+ params: { token: 'xyz' }
+ });
+ }, [connect]);
};
```
@@ -135,7 +135,7 @@ Optionally, if you would rather capture the response in a callback you can (or b
```ts
const { data } = useEvent(channel, 'join', (data) => {
- console.log(response);
+ console.log(response);
});
```
@@ -215,8 +215,8 @@ users[0].metas.lastSeen;
const [channel, { leave }] = useChannel('chat:lobby');
useEffect(() => {
- return () => {
- leave();
- };
+ return () => {
+ leave();
+ };
}, [leave]);
```
diff --git a/rollup.config.js b/rollup.config.js
index f66dfcf..e130e45 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -10,36 +10,36 @@ import pkg from './package.json' assert { type: 'json' };
const isDev = process.env.BUILD !== 'production';
const cjs = {
- file: pkg.main,
- format: 'cjs',
- exports: 'named',
- sourcemap: true,
- plugins: !isDev && [terser()]
+ file: pkg.main,
+ format: 'cjs',
+ exports: 'named',
+ sourcemap: true,
+ plugins: !isDev && [terser()]
};
const esm = {
- file: pkg.module,
- format: 'esm',
- exports: 'named',
- sourcemap: true
+ file: pkg.module,
+ format: 'esm',
+ exports: 'named',
+ sourcemap: true
};
const extensions = ['.js', '.ts', '.tsx', '.json'];
const plugins = [
typescript(),
- resolve({ extensions }),
- commonjs(),
- babel({ exclude: 'node_modules/**', extensions }),
- replace({
- 'preventAssignment': true,
- 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production')
- })
+ resolve({ extensions }),
+ commonjs(),
+ babel({ exclude: 'node_modules/**', extensions }),
+ replace({
+ 'preventAssignment': true,
+ 'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production')
+ })
].filter(Boolean);
export default {
- input: 'src/index.ts',
- output: isDev ? [esm] : [cjs, esm],
- plugins,
- external: Object.keys(pkg.peerDependencies)
+ input: 'src/index.ts',
+ output: isDev ? [esm] : [cjs, esm],
+ plugins,
+ external: Object.keys(pkg.peerDependencies)
};
diff --git a/src/PhoenixProvider.tsx b/src/PhoenixProvider.tsx
index 2bdc773..7f5d714 100644
--- a/src/PhoenixProvider.tsx
+++ b/src/PhoenixProvider.tsx
@@ -5,73 +5,73 @@ import { Socket } from 'phoenix';
import React, { useCallback, useEffect, useState } from 'react';
export type PhoenixProviderProps = {
- url?: string;
- options?: Partial;
- children?: React.ReactNode;
- onOpen?: () => void;
- onClose?: () => void;
- onError?: () => void;
+ url?: string;
+ options?: Partial;
+ children?: React.ReactNode;
+ onOpen?: () => void;
+ onClose?: () => void;
+ onError?: () => void;
};
export function PhoenixProvider({ url, options, ...props }: PhoenixProviderProps) {
- const { children, onOpen, onClose, onError } = props;
+ const { children, onOpen, onClose, onError } = props;
- const [socket, set] = useState(null);
- const [isConnected, setConnected] = useState(false);
+ const [socket, set] = useState(null);
+ const [isConnected, setConnected] = useState(false);
- const socketRef = useLatest(socket);
+ const socketRef = useLatest(socket);
- const defaultListeners = useCallback(
- (socket: PhoenixSocket) => {
- if (onOpen) socket.onOpen(onOpen);
- if (onClose) socket.onClose(onClose);
- if (onError) socket.onError(onError);
+ const defaultListeners = useCallback(
+ (socket: PhoenixSocket) => {
+ if (onOpen) socket.onOpen(onOpen);
+ if (onClose) socket.onClose(onClose);
+ if (onError) socket.onError(onError);
- socket.onOpen(() => {
- setConnected(true);
- });
+ socket.onOpen(() => {
+ setConnected(true);
+ });
- socket.onClose(() => {
- setConnected(false);
- });
- },
- [onClose, onError, onOpen]
- );
+ socket.onClose(() => {
+ setConnected(false);
+ });
+ },
+ [onClose, onError, onOpen]
+ );
- const connect = useCallback(
- (url: string, options?: Partial): PhoenixSocket => {
- const socket = new Socket(url, options ?? {}) as PhoenixSocket;
- socket.connect();
- set(socket);
- defaultListeners(socket);
+ const connect = useCallback(
+ (url: string, options?: Partial): PhoenixSocket => {
+ const socket = new Socket(url, options ?? {}) as PhoenixSocket;
+ socket.connect();
+ set(socket);
+ defaultListeners(socket);
- return socket;
- },
- [defaultListeners]
- );
+ return socket;
+ },
+ [defaultListeners]
+ );
- useEffect(() => {
- if (!url) return;
+ useEffect(() => {
+ if (!url) return;
- const socket = connect(url, options);
+ const socket = connect(url, options);
- return () => {
- socket.disconnect();
- };
- }, [url, options, connect]);
+ return () => {
+ socket.disconnect();
+ };
+ }, [url, options, connect]);
- return (
-
- {children}
-
- );
+ return (
+
+ {children}
+
+ );
}
PhoenixProvider.defaultProps = {
- options: {},
- onOpen: null,
- onClose: null,
- onError: null,
- connect: true,
- children: null
+ options: {},
+ onOpen: null,
+ onClose: null,
+ onError: null,
+ connect: true,
+ children: null
};
diff --git a/src/cache.ts b/src/cache.ts
index 0c24dc2..550c56f 100644
--- a/src/cache.ts
+++ b/src/cache.ts
@@ -3,30 +3,30 @@ import { ChannelMeta } from './useChannel';
export const cache = new Map>();
const defaultMeta: ChannelMeta = {
- data: null,
- status: 'joining',
- isSuccess: false,
- isLoading: true,
- isError: false,
- error: null
+ data: null,
+ status: 'joining',
+ isSuccess: false,
+ isLoading: true,
+ isError: false,
+ error: null
};
export default {
- insert: (topic: string, channelMeta: ChannelMeta) => {
- cache.set(topic, channelMeta);
- },
- get: (topic: string | undefined | boolean | null): ChannelMeta => {
- if (typeof topic !== 'string') return defaultMeta;
+ insert: (topic: string, channelMeta: ChannelMeta) => {
+ cache.set(topic, channelMeta);
+ },
+ get: (topic: string | undefined | boolean | null): ChannelMeta => {
+ if (typeof topic !== 'string') return defaultMeta;
- const result = cache.get(topic);
+ const result = cache.get(topic);
- if (result) {
- return result;
- } else {
- return defaultMeta;
- }
- },
- delete: (topic: string): boolean => {
- return cache.delete(topic);
- }
+ if (result) {
+ return result;
+ } else {
+ return defaultMeta;
+ }
+ },
+ delete: (topic: string): boolean => {
+ return cache.delete(topic);
+ }
};
diff --git a/src/index.ts b/src/index.ts
index 861accc..6d045de 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,10 +4,6 @@ export { useEvent } from './useEvent';
export { usePhoenix } from './usePhoenix';
export { usePresence } from './usePresence';
-export type {
- Channel,
- ChannelMeta,
- ChannelState
-} from './useChannel';
+export type { Channel, ChannelMeta, ChannelState } from './useChannel';
export type { PhoenixSocket, SocketConnectOption } from './usePhoenix';
diff --git a/src/useChannel/types.ts b/src/useChannel/types.ts
index 8e80232..3763ad2 100644
--- a/src/useChannel/types.ts
+++ b/src/useChannel/types.ts
@@ -3,37 +3,40 @@ import { Merge } from '../util';
export type { Push } from 'phoenix';
export { Channel } from 'phoenix';
-export type ChannelState = Merge, { leave: () => void; push: PushFunction }>;
+export type ChannelState = Merge<
+ ChannelMeta,
+ { leave: () => void; push: PushFunction }
+>;
export type PushEvent = {
- event: string;
- data?: Record;
-}
+ event: string;
+ data?: Record;
+};
export type ChannelStatus =
- | 'joining'
- | 'success'
- | 'error'
- | 'internal server error'
- | 'connection timeout'
- | 'closed';
+ | 'joining'
+ | 'success'
+ | 'error'
+ | 'internal server error'
+ | 'connection timeout'
+ | 'closed';
export type ChannelMeta = {
- data: JoinResposne | null;
- status: ChannelStatus;
- isSuccess: boolean;
- isLoading: boolean;
- isError: boolean;
- error: any;
+ data: JoinResposne | null;
+ status: ChannelStatus;
+ isSuccess: boolean;
+ isLoading: boolean;
+ isError: boolean;
+ error: any;
};
-export type PushFunction = ((
- event: Event['event'],
- data?: Event['data']
-) => Promise);
+export type PushFunction = (
+ event: Event['event'],
+ data?: Event['data']
+) => Promise;
export type ChannelOptions = {
- params?: Params extends Record ? Params : undefined;
+ params?: Params extends Record ? Params : undefined;
};
export type ChannelParams = Record;
diff --git a/src/useEvent/types.ts b/src/useEvent/types.ts
index 5e3a37f..6e662ef 100644
--- a/src/useEvent/types.ts
+++ b/src/useEvent/types.ts
@@ -1,6 +1,6 @@
export type EventAction = {
- event: string;
- data: any;
+ event: string;
+ data: any;
};
export type UseEventListener = (response: EventResponse) => void;
diff --git a/src/useLatest.ts b/src/useLatest.ts
index 661bc9f..eef0b3c 100644
--- a/src/useLatest.ts
+++ b/src/useLatest.ts
@@ -2,7 +2,7 @@ import type { RefObject } from 'react';
import { useRef } from 'react';
export default (val: T): RefObject => {
- const ref = useRef(val);
- ref.current = val;
- return ref;
+ const ref = useRef(val);
+ ref.current = val;
+ return ref;
};
diff --git a/src/usePhoenix/types.ts b/src/usePhoenix/types.ts
index 2003afe..71a8b17 100644
--- a/src/usePhoenix/types.ts
+++ b/src/usePhoenix/types.ts
@@ -3,19 +3,19 @@ import { Channel, Socket } from 'phoenix';
export type PhoenixSocket = Socket & { channels: Channel[] };
export interface SocketConnectOption {
- binaryType: BinaryType;
- params: Record | (() => object);
- transport: new (endpoint: string) => object;
- timeout: number;
- heartbeatIntervalMs: number;
- longpollerTimeout: number;
- /** The function to encode outgoing messages, Defaults to JSON encoder */
- encode: (payload: object, callback: (encoded: any) => void | Promise) => void;
- decode: (payload: string, callback: (decoded: any) => void | Promise) => void;
- logger: (kind: string, message: string, data: any) => void;
- reconnectAfterMs: (tries: number) => number;
- rejoinAfterMs: (tries: number) => number;
- vsn: string;
+ binaryType: BinaryType;
+ params: Record | (() => object);
+ transport: new (endpoint: string) => object;
+ timeout: number;
+ heartbeatIntervalMs: number;
+ longpollerTimeout: number;
+ /** The function to encode outgoing messages, Defaults to JSON encoder */
+ encode: (payload: object, callback: (encoded: any) => void | Promise) => void;
+ decode: (payload: string, callback: (decoded: any) => void | Promise) => void;
+ logger: (kind: string, message: string, data: any) => void;
+ reconnectAfterMs: (tries: number) => number;
+ rejoinAfterMs: (tries: number) => number;
+ vsn: string;
}
/**
diff --git a/src/usePhoenix/usePhoenix.ts b/src/usePhoenix/usePhoenix.ts
index 31495ab..be2a462 100644
--- a/src/usePhoenix/usePhoenix.ts
+++ b/src/usePhoenix/usePhoenix.ts
@@ -2,13 +2,13 @@ import React from 'react';
import { ConnectFunction, PhoenixSocket } from './types';
export const PhoenixContext = React.createContext<{
- socket: PhoenixSocket | null;
- connect: ConnectFunction;
- isConnected: boolean;
+ socket: PhoenixSocket | null;
+ connect: ConnectFunction;
+ isConnected: boolean;
} | null>(null);
export const usePhoenix = () => {
- const context = React.useContext(PhoenixContext);
- if (context === null) throw new Error('usePhoenix must be used within a PhoenixProvider');
- return context;
+ const context = React.useContext(PhoenixContext);
+ if (context === null) throw new Error('usePhoenix must be used within a PhoenixProvider');
+ return context;
};
diff --git a/src/usePresence/types.ts b/src/usePresence/types.ts
index 4a03a94..91607a5 100644
--- a/src/usePresence/types.ts
+++ b/src/usePresence/types.ts
@@ -1,15 +1,15 @@
export type Metas = { phx_ref?: string }[];
export type PresenceState = {
- [id: string]: T & { metas: M };
+ [id: string]: T & { metas: M };
};
export type PresenceData = T & {
- id: string;
- metas: M;
+ id: string;
+ metas: M;
};
export type PresenceDiff = {
- joins: T & { metas: M };
- leaves: T & { metas: M };
+ joins: T & { metas: M };
+ leaves: T & { metas: M };
};
diff --git a/src/usePresence/usePresence.tsx b/src/usePresence/usePresence.tsx
index 2bb4ccd..d9135e0 100644
--- a/src/usePresence/usePresence.tsx
+++ b/src/usePresence/usePresence.tsx
@@ -5,55 +5,55 @@ import type { Metas, PresenceState } from './types';
import { Merge } from '../util';
export function usePresence(
- topic: string | undefined
+ topic: string | undefined
): Merge[] {
- const [_presence, _setPresence] = useState>({});
- const { socket } = usePhoenix();
-
- useEffect(() => {
- if (socket && topic) {
- const channel = socket.channel(topic, {});
-
- channel.on('presence_state', (newState) => {
- _setPresence((prevState) => {
- if (Object.keys(prevState).length === 0) return newState;
- return Presence.syncState(prevState, newState);
- });
- });
-
- channel.on('presence_diff', (newDiff) => {
- _setPresence((prevState) => {
- if (Object.keys(prevState).length === 0) return prevState;
- return Presence.syncDiff(prevState, newDiff);
- });
- });
-
- channel.join();
-
- return () => {
- channel.leave();
- _setPresence({});
- };
- }
-
- return () => {};
- }, [socket, _setPresence, topic]);
-
- const items = useMemo(
- () =>
- _presence
- ? Object.keys(_presence).map((key: string) => {
- let metas = _presence[key].metas;
-
- if (Array.isArray(metas) && metas.length === 1) {
- metas = metas[0];
- }
-
- return { id: key, ..._presence[key], metas };
- })
- : [],
- [_presence]
- ) as Merge[];
-
- return items;
+ const [_presence, _setPresence] = useState>({});
+ const { socket } = usePhoenix();
+
+ useEffect(() => {
+ if (socket && topic) {
+ const channel = socket.channel(topic, {});
+
+ channel.on('presence_state', (newState) => {
+ _setPresence((prevState) => {
+ if (Object.keys(prevState).length === 0) return newState;
+ return Presence.syncState(prevState, newState);
+ });
+ });
+
+ channel.on('presence_diff', (newDiff) => {
+ _setPresence((prevState) => {
+ if (Object.keys(prevState).length === 0) return prevState;
+ return Presence.syncDiff(prevState, newDiff);
+ });
+ });
+
+ channel.join();
+
+ return () => {
+ channel.leave();
+ _setPresence({});
+ };
+ }
+
+ return () => {};
+ }, [socket, _setPresence, topic]);
+
+ const items = useMemo(
+ () =>
+ _presence
+ ? Object.keys(_presence).map((key: string) => {
+ let metas = _presence[key].metas;
+
+ if (Array.isArray(metas) && metas.length === 1) {
+ metas = metas[0];
+ }
+
+ return { id: key, ..._presence[key], metas };
+ })
+ : [],
+ [_presence]
+ ) as Merge[];
+
+ return items;
}
diff --git a/src/util.ts b/src/util.ts
index 05e8a8c..ac26b98 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -3,37 +3,37 @@ import { PhoenixSocket } from './usePhoenix';
import { ChannelMeta } from './useChannel';
export const findChannel = (socket: PhoenixSocket | null, topic: string): Channel | undefined => {
- if (typeof topic !== 'string') return undefined;
- return socket?.channels.find((channel) => channel.topic === topic);
+ if (typeof topic !== 'string') return undefined;
+ return socket?.channels.find((channel) => channel.topic === topic);
};
export const createMeta = (
- isSuccess: ChannelMeta['isSuccess'],
- isLoading: ChannelMeta['isLoading'],
- isError: ChannelMeta['isError'],
- error: ChannelMeta['error'],
- data: ChannelMeta['data'],
- status: ChannelMeta['status']
+ isSuccess: ChannelMeta['isSuccess'],
+ isLoading: ChannelMeta['isLoading'],
+ isError: ChannelMeta['isError'],
+ error: ChannelMeta['error'],
+ data: ChannelMeta['data'],
+ status: ChannelMeta['status']
): ChannelMeta => ({
- isSuccess,
- isLoading,
- isError,
- error,
- data,
- status
+ isSuccess,
+ isLoading,
+ isError,
+ error,
+ data,
+ status
});
export const pushPromise = (push: Push): Promise =>
- new Promise((resolve, reject) => {
- push.receive('ok', resolve).receive('error', reject);
- });
+ new Promise((resolve, reject) => {
+ push.receive('ok', resolve).receive('error', reject);
+ });
export type Merge = {
- [K in keyof A | keyof B]: K extends keyof A & keyof B
- ? A[K] | B[K]
- : K extends keyof B
- ? B[K]
- : K extends keyof A
- ? A[K]
- : never;
+ [K in keyof A | keyof B]: K extends keyof A & keyof B
+ ? A[K] | B[K]
+ : K extends keyof B
+ ? B[K]
+ : K extends keyof A
+ ? A[K]
+ : never;
};