diff --git a/.changeset/grumpy-planes-attack.md b/.changeset/grumpy-planes-attack.md
new file mode 100644
index 0000000000..030a677ee5
--- /dev/null
+++ b/.changeset/grumpy-planes-attack.md
@@ -0,0 +1,16 @@
+---
+'@udecode/plate-comments': major
+---
+
+- Renamed the `comments` prop on CommentsProvider to `initialComments` to reflect the fact that updating its value after the initial render has no effect
+- Removed the following props from CommentsProvider, since they represent the internal state of the comments plugin and should not be controlled externally:
+ - `activeCommentId`
+ - `addingCommentId`
+ - `newValue`
+ - `focusTextarea`
+- The following props on CommentsProvider can now be updated after the initial render (whereas prior to this version, doing so had no effect):
+ - `myUserId`
+ - `users`
+ - `onCommentAdd`
+ - `onCommentUpdate`
+ - `onCommentDelete`
diff --git a/apps/www/content/docs/comments.mdx b/apps/www/content/docs/comments.mdx
index e28f7ccd3d..8e4cfc1192 100644
--- a/apps/www/content/docs/comments.mdx
+++ b/apps/www/content/docs/comments.mdx
@@ -276,21 +276,9 @@ An interface representing a user who can make comments.
### CommentsStoreState
-An interface representing the state of the comments store.
+An interface representing the internal state of the comments store.
-
- The unique ID of the currently logged in or active user.
-
-
- An object where each key is a user's unique ID and the corresponding value
- is an instance of the `CommentUser` type, representing the user's data.
-
-
- An object where each key is a comment's unique ID and the corresponding
- value is an instance of the `TComment` type, representing the comment's
- data.
-
The unique ID of the currently active or focused comment.
@@ -303,18 +291,6 @@ An interface representing the state of the comments store.
Indicates whether the comment textarea is focused.
-
- A function that is invoked when a new comment is added. It accepts a partial
- comment object, which must include the user's ID.
-
-
- A function that is invoked when a comment is updated. It accepts a comment
- object, which must include the comment's ID.
-
-
- A function that is invoked when a comment is deleted. It accepts the unique
- ID of the comment to be deleted.
-
### TComment
@@ -563,12 +539,33 @@ A div component for positioning comments in the editor.
### CommentsProvider
-A jotai provider for comments data and users data.
+A provider for comments data and users data.
-
-Extends `CommentsStoreState`.
-
+
+ The unique ID of the currently logged in or active user.
+
+
+ An object where each key is a user's unique ID and the corresponding value
+ is an instance of the `CommentUser` type, representing the user's data.
+
+
+ An object where each key is a comment's unique ID and the corresponding
+ value is an instance of the `TComment` type, representing the comment's
+ data.
+
+
+ A function that is invoked when a new comment is added. It accepts a partial
+ comment object, which must include the user's ID.
+
+
+ A function that is invoked when a comment is updated. It accepts a comment
+ object, which must include the comment's ID.
+
+
+ A function that is invoked when a comment is deleted. It accepts the unique
+ ID of the comment to be deleted.
+
### useActiveCommentNode
diff --git a/apps/www/src/lib/plate/demo/comments/CommentsProvider.tsx b/apps/www/src/lib/plate/demo/comments/CommentsProvider.tsx
index 7cdeba5f88..de2b27bc3b 100644
--- a/apps/www/src/lib/plate/demo/comments/CommentsProvider.tsx
+++ b/apps/www/src/lib/plate/demo/comments/CommentsProvider.tsx
@@ -5,7 +5,7 @@ import { CommentsProvider as CommentsProviderPrimitive } from '@udecode/plate-co
export function CommentsProvider({ children }: { children: ReactNode }) {
return (
diff --git a/apps/www/src/lib/plate/demo/values/tableValue.tsx b/apps/www/src/lib/plate/demo/values/tableValue.tsx
index ecf8252c97..e124c81caf 100644
--- a/apps/www/src/lib/plate/demo/values/tableValue.tsx
+++ b/apps/www/src/lib/plate/demo/values/tableValue.tsx
@@ -118,4 +118,4 @@ export const tableValue: any = (
{createSpanningTable()}
-);
\ No newline at end of file
+);
diff --git a/packages/comments/src/stores/comments/CommentsProvider.tsx b/packages/comments/src/stores/comments/CommentsProvider.tsx
index 71dbd639e3..181a8ea75c 100644
--- a/packages/comments/src/stores/comments/CommentsProvider.tsx
+++ b/packages/comments/src/stores/comments/CommentsProvider.tsx
@@ -1,4 +1,4 @@
-import React, { ReactNode } from 'react';
+import React, { createContext, ReactNode, useContext, useMemo } from 'react';
import {
createAtomStore,
getJotaiProviderInitialValues,
@@ -11,9 +11,7 @@ import {
import { CommentUser, TComment } from '../../types';
-export const SCOPE_COMMENTS = Symbol('comments');
-
-export interface CommentsStoreState {
+export interface CommentsContext {
/**
* Id of the current user.
*/
@@ -24,6 +22,16 @@ export interface CommentsStoreState {
*/
users: Record;
+ onCommentAdd: ((value: WithPartial) => void) | null;
+ onCommentUpdate:
+ | ((value: Pick & Partial>) => void)
+ | null;
+ onCommentDelete: ((id: string) => void) | null;
+}
+
+export const SCOPE_COMMENTS = Symbol('comments');
+
+export interface CommentsStoreState {
/**
* Comments data.
*/
@@ -39,26 +47,10 @@ export interface CommentsStoreState {
newValue: Value;
focusTextarea: boolean;
-
- onCommentAdd: ((value: WithPartial) => void) | null;
- onCommentUpdate:
- | ((value: Pick & Partial>) => void)
- | null;
- onCommentDelete: ((id: string) => void) | null;
}
export const { commentsStore, useCommentsStore } = createAtomStore(
{
- /**
- * Id of the current user.
- */
- myUserId: null,
-
- /**
- * Users data.
- */
- users: {},
-
/**
* Comments data.
*/
@@ -74,35 +66,83 @@ export const { commentsStore, useCommentsStore } = createAtomStore(
newValue: [{ type: 'p', children: [{ text: '' }] }],
focusTextarea: false,
-
- onCommentAdd: null,
- onCommentUpdate: null,
- onCommentDelete: null,
- } as CommentsStoreState,
+ } satisfies CommentsStoreState as CommentsStoreState,
{
name: 'comments',
scope: SCOPE_COMMENTS,
}
);
+export const CommentsContext = createContext({
+ myUserId: null,
+ users: {},
+ onCommentAdd: null,
+ onCommentUpdate: null,
+ onCommentDelete: null,
+});
+
+export interface CommentsProviderProps extends Partial {
+ initialComments?: CommentsStoreState['comments'];
+ children: ReactNode;
+}
+
export function CommentsProvider({
children,
- ...props
-}: Partial & { children: ReactNode }) {
+ initialComments,
+ myUserId = null,
+ users = {},
+ onCommentAdd = null,
+ onCommentUpdate = null,
+ onCommentDelete = null,
+}: CommentsProviderProps) {
return (
- {children}
+
+ {children}
+
);
}
export const useCommentsStates = () => useCommentsStore().use;
-export const useCommentsSelectors = () => useCommentsStore().get;
export const useCommentsActions = () => useCommentsStore().set;
+export const useCommentsSelectors = () => {
+ const context = useContext(CommentsContext);
+
+ const contextGetters = useMemo(
+ () =>
+ Object.fromEntries(
+ Object.entries(context).map(([key, value]) => [key, () => value])
+ ) as { [K in keyof CommentsContext]: () => CommentsContext[K] },
+ [context]
+ );
+
+ const storeGetters = useCommentsStore().get;
+
+ // Combine getters from context and store
+ return useMemo(
+ () => ({
+ ...contextGetters,
+ ...storeGetters,
+ }),
+ [contextGetters, storeGetters]
+ );
+};
+
export const useCommentById = (id?: string | null): TComment | null => {
const comments = useCommentsSelectors().comments();
if (!id) return null;