{"id":23938059,"url":"https://github.com/plebbit/plebbit-react-hooks","last_synced_at":"2025-04-12T11:17:16.223Z","repository":{"id":41143201,"uuid":"455697762","full_name":"plebbit/plebbit-react-hooks","owner":"plebbit","description":"Hooks for building React applications using plebbit","archived":false,"fork":false,"pushed_at":"2024-12-29T22:26:30.000Z","size":4021,"stargazers_count":6,"open_issues_count":23,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-12-29T23:20:16.579Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/plebbit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-02-04T21:11:15.000Z","updated_at":"2024-12-29T22:26:33.000Z","dependencies_parsed_at":"2024-04-03T20:24:07.566Z","dependency_job_id":"e9bd9957-9014-45db-8cd4-dcbbf5fc905c","html_url":"https://github.com/plebbit/plebbit-react-hooks","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plebbit%2Fplebbit-react-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plebbit%2Fplebbit-react-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plebbit%2Fplebbit-react-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/plebbit%2Fplebbit-react-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/plebbit","download_url":"https://codeload.github.com/plebbit/plebbit-react-hooks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232662596,"owners_count":18557477,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-06T02:18:09.866Z","updated_at":"2025-04-12T11:17:16.203Z","avatar_url":"https://github.com/plebbit.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"*Telegram group for this repo https://t.me/plebbitreact*\n\n### Docs:\n\n- [Hooks API](#hooks)\n- [Getting started](#getting-started)\n- Install, testing and building: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/testing.md\n- Mock content (for UI development): https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/mock-content.md\n- Algorithms: https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/algorithms.md\n- Schema (Types, IndexedDb and state management): https://github.com/plebbit/plebbit-react-hooks/blob/master/docs/schema.md\n- Types: https://github.com/plebbit/plebbit-react-hooks/blob/master/src/types.ts\n\n### Hooks\n\n#### Accounts Hooks\n```\nuseAccount(): Account | undefined\nuseAccountComment({commentIndex: string}): Comment // get a pending published comment by its index\nuseAccountComments({filter: AccountPublicationsFilter}): {accountComments: Comment[]} // export or display list of own comments\nuseAccountVotes({filter: AccountPublicationsFilter}): {accountVotes: Vote[]}  // export or display list of own votes\nuseAccountVote({commentCid: string}): Vote // know if you already voted on some comment\nuseAccountEdits({filer: AccountPublicationsFilter}):  {accountEdits: AccountEdit[]}\nuseAccountSubplebbits(): {accountSubplebbits: {[subplebbitAddress: string]: AccountSubplebbit}, onlyIfCached?: boolean}\nuseAccounts(): Account[]\nuseNotifications(): {notifications: Notification[], markAsRead: Function}\n```\n#### Comments Hooks\n```\nuseComment({commentCid: string, onlyIfCached?: boolean}): Comment\nuseReplies({comment: Comment, sortType?: string, flat?: boolean, repliesPerPage?: number, filter?: CommentsFilter}): {replies: Comment[], hasMore: boolean, loadMore: function, reset: function, updatedReplies: Comment[], bufferedReplies: Comment[]}\nuseComments({commentCids: string[], onlyIfCached?: boolean}): {comments: Comment[]}\nuseEditedComment({comment: Comment}): {editedComment: Comment | undefined}\nuseValidateComment({comment: Comment, validateReplies?: boolean}): {valid: boolean}\n```\n#### Subplebbits Hooks\n```\nuseSubplebbit({subplebbitAddress: string, onlyIfCached?: boolean}): Subplebbit\nuseSubplebbits({subplebbitAddresses: string[], onlyIfCached?: boolean}): {subplebbits: Subplebbits[]}\nuseSubplebbitStats({subplebbitAddress: string, onlyIfCached?: boolean}): SubplebbitStats\nuseResolvedSubplebbitAddress({subplebbitAddress: string, cache: boolean}): {resolvedAddress: string | undefined} // use {cache: false} when checking the user's own subplebbit address\n```\n#### Authors Hooks\n```\nuseAuthor({authorAddress: string, commentCid: string}): {author: Author | undefined}\nuseAuthorAddress({comment: Comment}): {authorAddress: string | undefined, shortAuthorAddress: string | undefined, authorAddressChanged: boolean}\nuseAuthorComments({authorAddress: string, commentCid: string, filter?: CommentsFilter}): {authorComments: Comment[], hasMore: boolean, loadMore: Promise\u003cvoid\u003e}\nuseResolvedAuthorAddress({author?: Author, cache?: boolean}): {resolvedAddress: string | undefined} // use {cache: false} when checking the user's own author address\nuseAuthorAvatar({author?: Author}): {imageUrl: string | undefined}\nsetAuthorAvatarsWhitelistedTokenAddresses(tokenAddresses: string[])\n```\n#### Feeds Hooks\n```\nuseFeed({subplebbitAddresses: string[], sortType?: string, postsPerPage?: number, filter: CommentsFilter, newerThan: number}): {feed: Comment[], loadMore: function, hasMore: boolean, reset: function, updatedFeed: Comment[], bufferedFeed: Comment[], subplebbitAddressesWithNewerPosts: string[]}\nuseBufferedFeeds({feedsOptions: UseFeedOptions[]}) // preload or buffer feeds in the background, so they load faster when you call `useFeed`\n```\n#### Actions Hooks\n```\nuseSubscribe({subplebbitAddress: string}): {subscribed: boolean | undefined, subscribe: Function, unsubscribe: Function}\nuseBlock({address?: string, cid?: string}): {blocked: boolean | undefined, block: Function, unblock: Function}\nusePublishComment(options: UsePublishCommentOptions): {index: number, ...UsePublishCommentResult}\nusePublishVote(options: UsePublishVoteOptions): UsePublishVoteResult\nusePublishCommentEdit(options: UsePublishCommentEditOptions): UsePublishCommentEditResult\nusePublishCommentModeration(options: UsePublishCommentModerationOptions): UsePublishCommentModerationResult\nusePublishSubplebbitEdit(options: UsePublishSubplebbitEditOptions): UsePublishSubplebbitEditResult\nuseCreateSubplebbit(options: CreateSubplebbitOptions): {createdSubplebbit: Subplebbit | undefined, createSubplebbit: Function}\n```\n#### States Hooks\n```\nuseClientsStates({comment?: Comment, subplebbit?: Subplebbit}): {states, peers}\nuseSubplebbitsStates({subplebbitAddresses: string[]}): {states, peers}\n```\n#### Plebbit RPC Hooks\n```\nusePlebbitRpcSettings(): {plebbitRpcSettings: {plebbitOptions, challenges}, setPlebbitRpcSettings: Function}\n```\n#### Actions with no hooks implementations yet\n```\ncreateAccount(account: Account)\ndeleteAccount(accountName: string)\nsetAccount(account: Account)\nsetActiveAccount(accountName: string)\nsetAccountsOrder(accountNames: string[])\nimportAccount(serializedAccount: string)\nexportAccount(accountName: string): string // don't allow undefined to prevent catastrophic bugs\ndeleteSubplebbit(subplebbitAddress: string, accountName?: string)\n```\n#### Util functions\n```\nsetPlebbitJs(PlebbitJs) // set which plebbit-js version to use, e.g. to mock content for frontend dev or to use the node version in Electron\ndeleteDatabases() // delete all databases, including all caches and accounts data\ndeleteCaches() // delete the cached comments, cached subplebbits and cached pages only, no accounts data\n```\n\n#### Getting started\n\n```jsx\nimport {useComment, useAccount} from '@plebbit/plebbit-react-hooks'\n\nconst account = useAccount()\nconst comment = useComment({commentCid})\n```\n\n#### Get the active account, if none exist in browser database, a default account is generated\n\n```jsx\nconst account = useAccount()\n```\n\n#### Create accounts and change active account\n\n```jsx\nimport {useAccount, useAccounts, createAccount, setActiveAccount} from '@plebbit/plebbit-react-hooks'\n\nconst account = useAccount()\nconst {accounts} = useAccounts()\n\n// on first render\nconsole.log(accounts.length) // 1\nconsole.log(account.name) // 'Account 1'\n\nawait createAccount() // create 'Account 2'\nawait createAccount() // create 'Account 3'\nawait setActiveAccount('Account 3')\n\n// on render after updates\nconsole.log(accounts.length) // 3\nconsole.log(account.name) // 'Account 3'\n\n// you are now publishing from 'Account 3' because it is the active one\nconst {publishComment} = usePublishComment(publishCommentOptions)\nawait publishComment()\n```\n\n#### Get a post\n\n```jsx\nconst post = useComment({commentCid})\n\n// post.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress\nconst {authorAddress, shortAuthorAddress} = useAuthorAddress({comment: post})\n// exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress\n\n// use many times in a page without affecting performance\nconst post = useComment({commentCid, onlyIfCached: true})\n\n// post.replies are not validated, to show replies\nconst {replies, hasMore, loadMore} = useReplies({comment: post})\n\n// to show a preloaded reply without rerenders, validate manually\nconst {valid} = useValidateComment({comment: post.replies.pages.best.comments[0]})\nif (valid === false) {\n  // don't show this reply, it's malicious\n}\n// won't cause any rerenders if true\n```\n\n#### Get a comment\n\n```jsx\nconst comment = useComment({commentCid})\nconst {comments} = useComments({commentCids: [commentCid1, commentCid2, commentCid3]})\n\n// content\nconsole.log(comment.content || comment.link || comment.title)\n\n// comment.author.address should not be used directly, it needs to be verified asynchronously using useAuthorAddress\nconst {authorAddress, shortAuthorAddress} = useAuthorAddress({comment})\n// exception: when linking to an author profile page, /u/${comment.author.address}/c/${comment.cid} should be used, not useAuthorAddress({comment}).authorAddress\n\n// use without affecting performance\nconst {comments} = useComments({commentCids, onlyIfCached: true})\n```\n\n#### Get author avatar\n\n```jsx\nconst comment = useComment({commentCid})\n\n// get the nft avatar image url of the comment author\nconst {imageUrl, state, error, chainProvider, metadataUrl} = useAuthorAvatar({author: comment.author})\n\n// result\nif (state === 'succeeded') {\n  console.log('Succeeded getting avatar image URL', imageUrl)\n}\nif (state === 'failed') {\n  console.log('Failed getting avatar image URL', error.message)\n}\n\n// pending\nif (state === 'fetching-owner') {\n  console.log('Fetching NFT owner address from chain provider', chainProvider.urls)\n}\nif (state === 'fetching-uri') {\n  console.log('Fetching NFT URI from chain provider URL', chainProvider.urls)\n}\nif (state === 'fetching-metadata') {\n  console.log('Fetching NFT URI from', metadataUrl)\n}\n```\n\n#### Get author profile page\n\n```jsx\n// NOTE: you must have a comment cid from the author to load his profile page\n// e.g. the page url would be /#/u/\u003cauthorAddress\u003e/c/\u003ccommentCid\u003e\nconst authorResult = useAuthor({commentCid, authorAddress})\nconst {imageUrl} = useAuthorAvatar({author: authorResult.author})\nconst {authorComments, lastCommentCid, hasMore, loadMore} = useAuthorComments({commentCid, authorAddress})\n\n// result\nif (authorResult.state === 'succeeded') {\n  console.log('Succeeded getting author', authorResult.author)\n}\nif (state === 'failed') {\n  console.log('Failed getting author', authorResult.error.message)\n}\n\n// listing the author comments with infinite scroll\nimport {Virtuoso} from 'react-virtuoso'\n\n\u003cVirtuoso\n  data={authorComments}\n  itemContent={(index, comment) =\u003e \u003cComment index={index} comment={comment}/\u003e}\n  useWindowScroll={true}\n  components={{Footer: hasMore ? () =\u003e \u003cLoading/\u003e : undefined}}\n  endReached={loadMore}\n  increaseViewportBy={{bottom: 600, top: 600}}\n/\u003e\n\n// it is recommended to always redirect the user to the last known comment cid\n// in case they want to share the url with someone, the author's comments\n// will load faster when using the last comment cid\nimport {useParams} from 'react-router-dom'\nconst params = useParams()\n\nuseEffect(() =\u003e {\n  if (lastCommentCid \u0026\u0026 params.comentCid !== lastCommentCid) {\n    history.push(`/u/${params.authorAddress}/c/${lastCommentCid}`);\n  }\n}, [lastCommentCid])\n\n// search an author's comments\nconst createSearchFilter = (searchTerm) =\u003e ({\n  filter: (comment) =\u003e comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm),\n  key: `includes-${searchTerm}` // required key to cache the filter\n})\nconst filter = createSearchFilter('bitcoin')\nconst {authorComments, lastCommentCid, hasMore, loadMore} = useAuthorComments({commentCid, authorAddress, filter})\n```\n\n#### Get a subplebbit\n\n```jsx\nconst subplebbit = useSubplebbit({subplebbitAddress})\nconst subplebbitStats = useSubplebbitStats({subplebbitAddress})\nconst {subplebbits} = useSubplebbits({subplebbitAddresses: [subplebbitAddress, subplebbitAddress2, subplebbitAddress3]})\n\n// use without affecting performance\nconst {subplebbits} = useSubplebbits({subplebbitAddresses: [subplebbitAddress, subplebbitAddress2, subplebbitAddress3], onlyIfCached: true})\n\n// subplebbit.posts are not validated, to show posts\nconst {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: [subplebbitAddress]})\n\n// to show a preloaded post without rerenders, validate manually\nconst {valid} = useValidateComment({comment: subplebbit.posts.pages.topAll.comments[0]})\nif (valid === false) {\n  // don't show this post, it's malicious\n}\n// won't cause any rerenders if true\n```\n\n#### Create a post or comment using callbacks\n\n```jsx\nconst onChallenge = async (challenges: Challenge[], comment: Comment) =\u003e {\n  let challengeAnswers: string[]\n  try {\n    // ask the user to complete the challenges in a modal window\n    challengeAnswers = await getChallengeAnswersFromUser(challenges)\n  }\n  catch (e) {\n    // if he declines, throw error and don't get a challenge answer\n  }\n  if (challengeAnswers) {\n    // if user declines, publishChallengeAnswers is not called, retry loop stops\n    await comment.publishChallengeAnswers(challengeAnswers)\n  }\n}\n\nconst onChallengeVerification = (challengeVerification, comment) =\u003e {\n  // if the challengeVerification fails, a new challenge request will be sent automatically\n  // to break the loop, the user must decline to send a challenge answer\n  // if the subplebbit owner sends more than 1 challenge for the same challenge request, subsequents will be ignored\n  if (challengeVerification.challengeSuccess === true) {\n    console.log('challenge success', {publishedCid: challengeVerification.publication.cid})\n  }\n  else if (challengeVerification.challengeSuccess === false) {\n    console.error('challenge failed', {reason: challengeVerification.reason, errors: challengeVerification.errors})\n  }\n}\n\nconst onError = (error, comment) =\u003e console.error(error)\n\nconst publishCommentOptions = {\n  content: 'hello',\n  title: 'hello',\n  subplebbitAddress: '12D3KooW...',\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\n\nconst {index, state, publishComment} = usePublishComment(publishCommentOptions)\n\n// create post\nawait publishComment()\n// pending comment index\nconsole.log(index)\n// pending comment state\nconsole.log(state)\n\n// after publishComment is called, the account comment index gets defined\n// it is recommended to immediately redirect the user to a page displaying\n// the user's comment with a \"pending\" label\nif (index !== undefined) {\n  history.push(`/profile/c/${index}`)\n  // on the \"pending\" comment page, you can get the pending comment by doing\n  // const accountComment = useAccountComment({commentIndex: index})\n  // after accountComment.cid gets defined, it means the comment was published successfully\n  // it is recommended to immediately redirect to `/p/${accountComment.subplebbitAddress}/c/${useAccountComment.cid}`\n}\n\n// reply to a post or comment\nconst publishReplyOptions = {\n  content: 'hello',\n  parentCid: 'Qm...', // the cid of the comment to reply to\n  subplebbitAddress: '12D3KooW...',\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\nconst {publishComment} = usePublishComment(publishReplyOptions)\nawait publishComment()\n\n// when displaying replies, it is recommended to include the user's pending replies\n// https://github.com/plebbit/plebbit-react-hooks/#get-replies-to-a-post-nested (nested)\n// https://github.com/plebbit/plebbit-react-hooks/#get-replies-to-a-post-flattened-not-nested (not nested)\n```\n\n#### Create a post or comment using hooks\n\n```jsx\nconst publishCommentOptions = {\n  content: 'hello',\n  title: 'hello',\n  subplebbitAddress: '12D3KooW...',\n}\n\nconst {index, state, publishComment, challenge, challengeVerification, publishChallengeAnswers, error} = usePublishComment(publishCommentOptions)\n\nif (challenge) {\n  // display challenges to user and call publishChallengeAnswers(challengeAnswers)\n}\n\nif (challengeVerification) {\n  // display challengeVerification.challengeSuccess to user\n  // redirect to challengeVerification.publication.cid\n}\n\nif (error) {\n  // display error to user\n}\n\n// after publishComment is called, the account comment index gets defined\n// it is recommended to immediately redirect the user to a page displaying\n// the user's comment with a \"pending\" label\nif (index !== undefined) {\n  history.push(`/profile/c/${index}`)\n  // on the \"pending\" comment page, you can get the pending comment by doing\n  // const accountComment = useAccountComment({commentIndex: index})\n  // after accountComment.cid gets defined, it means the comment was published successfully\n  // it is recommended to immediately redirect to `/p/${accountComment.subplebbitAddress}/c/${useAccountComment.cid}`\n}\n\n// create post\nawait publishComment()\n```\n\n#### Create a post or comment anonymously (without account.signer or account.author)\n\n```jsx\nconst account = useAccount()\nconst signer = await account.plebbit.createSigner()\n\nconst publishCommentOptions = {\n  content: 'hello',\n  title: 'hello',\n  subplebbitAddress: '12D3KooW...',\n  // use a newly generated author address (optional)\n  signer,\n  // use a different display name (optional)\n  author: {\n    displayName: 'Esteban',\n    address: signer.address\n  }\n}\n\nconst {publishComment} = usePublishComment(publishCommentOptions)\nawait publishComment()\n```\n\n#### Create a vote\n\n```jsx\nconst commentCid = 'QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui'\nconst publishVoteOptions = {\n  commentCid,\n  vote: 1,\n  subplebbitAddress: 'news.eth',\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\nconst {state, error, publishVote} = usePublishVote(publishVoteOptions)\n\nawait publishVote()\nconsole.log(state)\nconsole.log(error)\n\n// display the user's vote\nconst {vote} = useAccountVote({commentCid})\n\nif (vote === 1)\n  console.log('user voted 1')\nif (vote === -1)\n  console.log('user voted -1')\nif (vote === 0)\n  console.log('user voted 0')\nif (vote === undefined)\n  console.log(`user didn't vote yet`)\n```\n\n#### Create a comment edit\n\n```jsx\nconst publishCommentEditOptions = {\n  commentCid: 'QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui',\n  content: 'edited content',\n  subplebbitAddress: 'news.eth',\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\nconst {state, error, publishCommentEdit} = usePublishCommentEdit(publishCommentEditOptions)\n\nawait publishCommentEdit()\nconsole.log(state)\nconsole.log(error)\n\n// view the status of a comment edit instantly\nlet comment = useComment({commentCid: publishCommentEditOptions.commentCid})\nconst {state: editedCommentState, editedComment} = useEditedComment({comment})\n\n// if the comment has a succeeded, failed or pending edit, use the edited comment\nif (editedComment) {\n  comment = editedComment\n}\n\nlet editLabel\nif (editedCommentState === 'succeeded') {\n  editLabel = {text: 'EDITED', color: 'green'}\n}\nif (editedCommentState === 'pending') {\n  editLabel = {text: 'PENDING EDIT', color: 'orange'}\n}\nif (editedCommentState === 'failed') {\n  editLabel = {text: 'FAILED EDIT', color: 'red'}\n}\n```\n\n#### Create a comment moderation\n\n```jsx\nconst publishCommentModerationOptions = {\n  commentCid: 'QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui',\n  subplebbitAddress: 'news.eth',\n  commentModeration: {locked: true},\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\nconst {state, error, publishCommentModeration} = usePublishCommentModeration(publishCommentModerationOptions)\n\nawait publishCommentModeration()\nconsole.log(state)\nconsole.log(error)\n\n// view the status of a comment moderation instantly\nlet comment = useComment({commentCid: publishCommentModerationOptions.commentCid})\nconst {state: editedCommentState, editedComment} = useEditedComment({comment})\n\n// if the comment has a succeeded, failed or pending edit, use the edited comment\nif (editedComment) {\n  comment = editedComment\n}\n\nlet editLabel\nif (editedCommentState === 'succeeded') {\n  editLabel = {text: 'EDITED', color: 'green'}\n}\nif (editedCommentState === 'pending') {\n  editLabel = {text: 'PENDING EDIT', color: 'orange'}\n}\nif (editedCommentState === 'failed') {\n  editLabel = {text: 'FAILED EDIT', color: 'red'}\n}\n```\n\n#### Delete a comment\n\n```jsx\nconst publishCommentEditOptions = {\n  commentCid: 'QmZVYzLChjKrYDVty6e5JokKffGDZivmEJz9318EYfp2ui',\n  removed: true,\n  subplebbitAddress: 'news.eth',\n  onChallenge,\n  onChallengeVerification,\n  onError\n}\nconst {state, error, publishCommentEdit} = usePublishCommentEdit(publishCommentEditOptions)\n\nawait publishCommentEdit()\nconsole.log(state)\nconsole.log(error)\n\n// TODO: implement accountActions.deleteComment to remove your comment from your local accountComments database\n```\n\n#### Subscribe to a subplebbit\n\n```jsx\nlet subplebbitAddress = 'news.eth'\nsubplebbitAddress = '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z'\nsubplebbitAddress = 'tech.eth'\nconst {subscribed, subscribe, unsubscribe} = useSubscribe({subplebbitAddress})\nawait subscribe()\nconsole.log(subscribed) // true\n\n// view subscriptions\nconst account = useAccount()\nconsole.log(account.subscriptions) // ['news.eth', '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', 'tech.eth']\n\n// unsubscribe\nawait unsubscribe()\n\n// get a feed of subscriptions\nconst {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: account.subscriptions, sortType: 'topAll'})\nconsole.log(feed)\n```\n\n#### Get feed\n\n```jsx\nimport {Virtuoso} from 'react-virtuoso'\nconst {feed, hasMore, loadMore} = useFeed({subplebbitAddresses: ['memes.eth', '12D3KooW...', '12D3KooW...'], sortType: 'topAll'})\n\n\u003cVirtuoso\n  data={feed}\n  itemContent={(index, post) =\u003e \u003cPost index={index} post={post}/\u003e}\n  useWindowScroll={true}\n  components={{Footer: hasMore ? () =\u003e \u003cLoading/\u003e : undefined}}\n  endReached={loadMore}\n  increaseViewportBy={{bottom: 600, top: 600}}\n/\u003e\n\n// you probably will want to buffer some feeds in the background so they are already loaded\n// when you need them\nuseBufferedFeeds({\n  feedsOptions: [\n    {subplebbitAddresses: ['news.eth', 'crypto.eth'], sortType: 'new'},\n    {subplebbitAddresses: ['memes.eth'], sortType: 'topWeek'},\n    {subplebbitAddresses: ['12D3KooW...', '12D3KooW...', '12D3KooW...', '12D3KooW...'], sortType: 'hot'}\n  ]\n})\n\n// search a feed\nconst createSearchFilter = (searchTerm) =\u003e ({\n  filter: (comment) =\u003e comment.title?.includes(searchTerm) || comment.content?.includes(searchTerm),\n  key: `includes-${searchTerm}` // required key to cache the filter\n})\nconst filter = createSearchFilter('bitcoin')\nconst {feed, hasMore, loadMore} = useFeed({subplebbitAddresses, filter})\n\n// image only feed\nconst filter = {\n  filter: (comment) =\u003e getCommentLinkMediaType(comment?.link) === 'image',\n  key: 'image-only' // required key to cache the filter\n}\nconst {feed, hasMore, loadMore} = useFeed({subplebbitAddresses, filter})\n```\n\n#### Edit an account\n\n```jsx\nimport {useAccount, setAccount, useResolvedAuthorAddress} from '@plebbit/plebbit-react-hooks'\nconst account = useAccount() // or useAccount('Account 2') to use an account other than the active one\n\nconst author: {...account.author, displayName: 'John'}\nconst editedAccount = {...account, author}\n\nawait setAccount(editedAccount)\n\n// check if the user has set his ENS name properly, use {cache: false} or it won't update\nconst author = {...account.author, address: 'username.eth'}\n// authorAddress should equal to account.signer.address\nconst {resolvedAddress, state, error, chainProvider} = useResolvedAuthorAddress({author, cache: false}) \n\n// result\nif (state === 'succeeded') {\n  console.log('Succeeded resolving address', resolvedAddress)\n}\nif (state === 'failed') {\n  console.log('Failed resolving address', error.message)\n}\n\n// pending\nif (state === 'resolving') {\n  console.log('Resolving address from chain provider URL', chainProvider.urls)\n}\n```\n\n#### Delete account\n\n\u003e Note: deleting account is unrecoverable, warn the user to export/backup his account before deleting\n\n```jsx\nimport {deleteAccount} from '@plebbit/plebbit-react-hooks'\n\n// delete active account\nawait deleteAccount()\n\n// delete account by name\nawait deleteAccount('Account 2')\n```\n\n#### Get your own comments and votes\n\n```jsx\n// all my own comments\nconst {accountComments} = useAccountComments()\nfor (const accountComment of accountComments) {\n  // it is recommended to show a label in the UI if accountComment.state is 'pending' or 'failed'\n  console.log('comment', accountComment.index, 'is status', accountComment.state)\n}\n\n// all my own votes\nconst {accountVotes} = useAccountVotes()\n\n// my own comments in memes.eth\nconst subplebbitAddress = 'memes.eth'\nconst filter = useCallback((comment) =\u003e comment.subplebbitAddress === subplebbitAddress, [subplebbitAddress]) // important to use useCallback or the same function or will cause rerenders\nconst myCommentsInMemesEth = useAccountComments({filter})\n\n// my own posts in memes.eth\nconst filter = useCallback((comment) =\u003e comment.subplebbitAddress === subplebbitAddress \u0026\u0026 !comment.parentCid, [subplebbitAddress])\nconst myPostsInMemesEth = useAccountComments({filter})\n\n// my own replies in a post with cid 'Qm...'\nconst postCid = 'Qm...'\nconst filter = useCallback((comment) =\u003e comment.postCid === postCid, [postCid])\nconst myCommentsInSomePost = useAccountComments({filter})\n\n// my own replies to a comment with cid 'Qm...'\nconst parentCommentCid = 'Qm...'\nconst filter = useCallback((comment) =\u003e comment.parentCid === parentCommentCid, [parentCommentCid])\nconst myRepliesToSomeComment = useAccountComments({filter})\n\n// know if you upvoted a comment already with cid 'Qm...'\nconst {vote} = useAccountVote({commentCid: 'Qm...'})\nconsole.log(vote) // 1, -1 or 0\n```\n\n#### Determine if a comment is your own\n\n```jsx\nconst account = useAccount()\nconst comment = useComment({commentCid})\nconst isMyOwnComment = account?.author.address === comment?.author.address\n```\n\n#### Get account notifications\n\n```jsx\nconst {notifications, markAsRead} = useNotifications()\nfor (const notification of notifications) {\n  console.log(notification)\n}\nawait markAsRead()\n\nconst johnsNotifications = useNotifications({accountName: 'John'})\nfor (const notification of johnsNotifications.notifications) {\n  console.log(notification)\n}\nawait johnsNotifications.markAsRead()\n\n// get the unread notification counts for all accounts\nconst {accounts} = useAccounts()\nconst accountsUnreadNotificationsCounts = accounts?.map(account =\u003e account.unreadNotificationCount)\n```\n\n#### Block an address (author, subplebbit or multisub)\n\n```jsx\nconst address: 'subplebbit-address.eth' // or 'author-address.eth' or '12D3KooW...'\nconst {blocked, unblock, block} = useBlock({address})\n\nif (blocked) {\n  console.log(`'${address}' is blocked`)\n}\nelse {\n  console.log(`'${address}' is not blocked`)\n}\n\n// to block\nblock()\n\n// to unblock\nunblock()\n```\n\n#### Block a cid (hide a comment)\n\n```jsx\nconst {blocked, unblock, block} = useBlock({cid: 'Qm...'})\n\nif (blocked) {\n  console.log(`'${cid}' is blocked`)\n}\nelse {\n  console.log(`'${cid}' is not blocked`)\n}\n\n// to block\nblock()\n\n// to unblock\nunblock()\n```\n\n#### (Desktop only) Create a subplebbit\n\n```jsx\nconst createSubplebbitOptions = {title: 'My subplebbit title'}\nconst {createdSubplebbit, createSubplebbit} = useCreateSubplebbit(createSubplebbitOptions)\nawait createSubplebbit()\n\n// it is recommended to redirect to `p/${createdSubplebbit.address}` after creation\nif (createdSubplebbit?.address) {\n  console.log('created subplebbit with title', createdSubplebbit.title)\n  history.push(`/p/${createdSubplebbit.address}`)\n}\n\n// after the subplebbit is created, fetch it using\nconst {accountSubplebbits} = useAccountSubplebbits()\nconst accountSubplebbitAddresses = Object.keys(accountSubplebbits)\nconst subplebbits = useSubplebbits({subplebbitAddresses: accountSubplebbitAddresses})\n// or\nconst _subplebbit = useSubplebbit({subplebbitAddress: createdSubplebbit.address})\n```\n\n#### (Desktop only) List the subplebbits you created\n\n```jsx\nconst {accountSubplebbits} = useAccountSubplebbits()\nconst ownerSubplebbitAddresses = Object.keys(accountSubplebbits).map(subplebbitAddress =\u003e accountSubplebbits[subplebbitAddress].role === 'owner')\nconst subplebbits = useSubplebbits({subplebbitAddresses: ownerSubplebbitAddresses})\n```\n\n#### (Desktop only) Edit your subplebbit settings\n\n```jsx\nconst onChallenge = async (challenges: Challenge[], subplebbitEdit: SubplebbitEdit) =\u003e {\n  let challengeAnswers: string[]\n  try {\n    challengeAnswers = await getChallengeAnswersFromUser(challenges)\n  }\n  catch (e) {}\n  if (challengeAnswers) {\n    await subplebbitEdit.publishChallengeAnswers(challengeAnswers)\n  }\n}\n\nconst onChallengeVerification = (challengeVerification, subplebbitEdit) =\u003e {\n  console.log('challenge verified', challengeVerification)\n}\n\nconst onError = (error, subplebbitEdit) =\u003e console.error(error)\n\n// add ENS to your subplebbit\nconst editSubplebbitOptions = {\n  subplebbitAddress: '12D3KooWANwdyPERMQaCgiMnTT1t3Lr4XLFbK1z4ptFVhW2ozg1z', // the previous address before changing it\n  address: 'your-subplebbit-address.eth', // the new address to change to\n  onChallenge, \n  onChallengeVerification,\n  onError\n}\n\nawait publishSubplebbitEdit()\n\n// edit other subplebbit settings\nconst editSubplebbitOptions = {\n  subplebbitAddress: 'your-subplebbit-address.eth', // the address of the subplebbit to change\n  title: 'Your title', \n  description: 'Your description',\n  onChallenge, \n  onChallengeVerification,\n  onError\n}\nconst {publishSubplebbitEdit} = usePublishSubplebbitEdit(editSubplebbitOptions)\nawait publishSubplebbitEdit()\n\n// verify if ENS was set correctly, use {cache: false} or it won't update\nconst {resolvedAddress} = useResolvedSubplebbitAddress({subplebbitAddress: 'your-subplebbit-address.eth', cache: false})\n\n// result\nif (state === 'succeeded') {\n  console.log('Succeeded resolving address', resolvedAddress)\n  console.log('ENS set correctly', resolvedAddress === subplebbit.signer.address)\n}\nif (state === 'failed') {\n  console.log('Failed resolving address', error.message)\n}\n\n// pending\nif (state === 'resolving') {\n  console.log('Resolving address from chain provider URL', chainProvider.urls)\n}\n```\n\n#### Export and import account\n\n```jsx\nimport {exportAccount, importAccount, setActiveAccount, setAccountsOrder} from '@plebbit/plebbit-react-hooks'\n\n// get active account 'Account 1'\nconst activeAccount = useAccount()\n\n// export active account, tell user to copy or download this json\nconst activeAccountJson = await exportAccount()\n\n// import account\nawait importAccount(activeAccountJson)\n\n// get imported account 'Account 1 2' (' 2' gets added to account.name if account.name already exists)\nconst importedAccount = useAccount('Account 1 2')\n\n// make imported account active account\nawait setActiveAccount('Account 1 2')\n\n// reorder the accounts list\nawait setAccountsOrder(['Account 1 2', 'Account 1'])\n```\n\n#### View the status of a comment edit\n\n```jsx\nlet comment = useComment({commentCid})\nconst {state: editedCommentState, editedComment} = useEditedComment({comment})\n\n// if the comment has a succeeded, failed or pending edit, use the edited comment\nif (editedComment) {\n  comment = editedComment\n}\n\nlet editLabel\nif (editedCommentState === 'succeeded') {\n  editLabel = {text: 'EDITED', color: 'green'}\n}\nif (editedCommentState === 'pending') {\n  editLabel = {text: 'PENDING EDIT', color: 'orange'}\n}\nif (editedCommentState === 'failed') {\n  editLabel = {text: 'FAILED EDIT', color: 'red'}\n}\n```\n\n#### View the status of a specific comment edit property\n\n```jsx\nconst comment = useComment({commentCid})\nconst editedComment = useEditedComment({comment})\nif (editedComment.failedEdits.removed !== undefined) {\n  console.log('failed editing comment.removed property')\n}\nif (editedComment.succeededEdits.removed !== undefined) {\n  console.log('succeeded editing comment.removed property')\n}\nif (editedCommentResult.pendingEdits.removed !== undefined) {\n  console.log('pending editing comment.removed property')\n}\n\n// view the full comment with all edited properties (both succeeded and pending)\nconsole.log(editedComment.editedComment)\n\n// view the state of all edits of the comment\nconsole.log(editedComment.state) // 'unedited' | 'succeeded' | 'pending' | 'failed'\n```\n\n#### List all comment and subplebbit edits the account has performed\n\n```jsx\nconst {accountEdits} = useAccountEdits()\nfor (const accountEdit of accountEdits) {\n  console.log(accountEdit)\n}\nconsole.log(`there's ${accountEdits.length} account edits`)\n\n// get only the account edits of a specific comment\nconst commentCid = 'Qm...'\nconst filter = useCallback((edit) =\u003e edit.commentCid === commentCid, [commentCid]) // important to use useMemo or the same function or will cause rerenders\nconst {accountEdits} = useAccountEdits({filter})\n\n// only get account edits in a specific subplebbit\nconst subplebbitAddress = 'news.eth'\nconst filter = useCallback((edit) =\u003e edit.subplebbitAddress === subplebbitAddress, [subplebbitAddress])\nconst {accountEdits} = useAccountEdits({filter})\n```\n\n#### Get replies to a post (nested)\n\n```jsx\nimport {useComment, useAccountComments} from '@plebbit/plebbit-react-hooks'\n\nconst useRepliesAndAccountReplies = (comment) =\u003e {\n  // filter only the parent cid\n  const filter = useCallback((accountComment) =\u003e accountComment.parentCid === (comment?.cid || 'n/a'), [comment?.cid])\n  const {accountComments} = useAccountComments({filter})\n\n  // the account's replies have a delay before getting published, so get them locally from accountComments instead\n  const accountRepliesNotYetPublished = useMemo(() =\u003e {\n    const replies = comment?.replies?.pages?.topAll?.comments || []\n    const replyCids = new Set(replies.map(reply =\u003e reply.cid))\n    // filter out the account comments already in comment.replies, so they don't appear twice\n    return accountComments.filter(accountReply =\u003e !replyCids.has(accountReply.cid))\n  }, [comment?.replies?.pages?.topAll?.comments, accountComments])\n\n  const repliesAndNotYetPublishedReplies = useMemo(() =\u003e {\n    return [\n      // put the author's unpublished replies at the top, latest first (reverse)\n      ...accountRepliesNotYetPublished.reverse(),\n      // put the published replies after,\n      ...comment?.replies?.pages?.topAll?.comments || []\n    ]\n  }, [comment?.replies?.pages?.topAll?.comments, accountRepliesNotYetPublished])\n\n  return repliesAndNotYetPublishedReplies\n}\n\nconst Reply = ({reply}) =\u003e {\n  const replies = useRepliesAndAccountReplies(reply)\n  return (\n    \u003cdiv\u003e\n      \u003cdiv\u003e{reply.author.address} {reply.timestamp}\u003c/div\u003e\n      {reply.state === 'pending' \u0026\u0026 \u003cdiv\u003ePENDING\u003c/div\u003e}\n      {reply.state === 'failed' \u0026\u0026 \u003cdiv\u003eFAILED\u003c/div\u003e}\n      \u003cdiv\u003e{reply.content}\u003c/div\u003e\n      \u003cdiv style={{marginLeft: 4}}\u003e\n        {replies.map(reply =\u003e \u003cReply reply={reply}/\u003e))}\n      \u003c/div\u003e\n    \u003c/div\u003e\n  )\n}\n\n// account replies can have reply.state 'pending' or 'failed' when they are not published yet\n// it is recommended to add a 'pending' or 'failed' label to these replies\nconst comment = useComment({commentCid})\nconst replies = useRepliesAndAccountReplies(comment)\nconst repliesComponents = replies.map(reply =\u003e \u003cReply reply={reply}/\u003e)\n```\n\n#### Get replies to a post flattened (not nested)\n\n```jsx\nimport {useComment, useAccountComments} from '@plebbit/plebbit-react-hooks'\nimport {flattenCommentsPages} from '@plebbit/plebbit-react-hooks/dist/lib/utils'\n\n// the post\nconst comment = useComment({commentCid})\n\n// the default way to display replies is nested, flatten (unnest) them instead\nconst flattenedReplies = useMemo(() =\u003e flattenCommentsPages(comment.replies), [comment.replies])\n\n// the account replies to the post (commentCid) and account replies to all the post's replies\nconst postAndRepliesCids = useMemo(() =\u003e new Set([(commentCid || 'n/a'), ...flattenedReplies.map(reply =\u003e reply.cid)]), [commentCid, flattenedReplies])\nconst filter = useCallback((accountComment) =\u003e postAndRepliesCids.has(accountComment.parentCid), [postAndRepliesCids])\nconst {accountComments} = useAccountComments({filter})\n\n// the account's replies have a delay before getting published, so get them locally from accountComments instead\nconst accountRepliesNotYetInCommentReplies = useMemo(() =\u003e {\n  const commentReplyCids = new Set(flattenedReplies.map(reply =\u003e reply.cid))\n  // filter out the account comments already in comment.replies, so they don't appear twice\n  return accountComments.filter(accountReply =\u003e !commentReplyCids.has(accountReply.cid))\n}, [flattenedReplies, accountComments])\n\n// merge the not yet published account replies and published replies, and sort them by timestamp\nconst sortedReplies = useMemo(() =\u003e [...accountRepliesNotYetInCommentReplies, ...flattenedReplies].sort((a, b) =\u003e a.timestamp - b.timestamp), [accountRepliesNotYetInCommentReplies, flattenedReplies])\n\nfor (const reply of sortedReplies) {\n  // account replies can have reply.state 'pending' or 'failed' when they are not published yet\n  // it is recommended to add a 'pending' or 'failed' label to these replies\n  console.log(reply)\n}\n```\n\n#### Get a shortCid or shortAddress\n\n```jsx\n// NOTE: not possible to do from plebbit-react-hooks, needs plebbit-js\nimport {getShortAddress, getShortCid} from '@plebbit/plebbit-js'\n\nconst shortParentCid = getShortAddress(comment.parentCid)\nconst shortAddress = getShortCid(address)\n```\n\n#### Get a shortCid or shortAddress\n\n```jsx\nimport {useShortAddress, useShortCid} from '@plebbit/plebbit-react-hooks'\n\nconst shortParentCid = useShortCid(comment.parentCid)\nconst shortAddress = useShortAddress(address)\n```\n\n#### useBufferedFeeds with concurrency\n\n```jsx\nconst useBufferedFeedsWithConcurrency = ({feedOptions}) =\u003e {\n\n  const subplebbits = useSubplebbits()\n\n  return useBufferedFeeds({feedsOptions})\n}\n\nconst feedOptions = [\n  {subplebbitAddresses: ['news.eth', 'crypto.eth'], sortType: 'new'},\n  {subplebbitAddresses: ['memes.eth'], sortType: 'topWeek'},\n  {subplebbitAddresses: ['12D3KooW...', '12D3KooW...', '12D3KooW...', '12D3KooW...'], sortType: 'hot'},\n  ...\n]\n\nuseBufferedFeedsWithConcurrency({feedOptions})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplebbit%2Fplebbit-react-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplebbit%2Fplebbit-react-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplebbit%2Fplebbit-react-hooks/lists"}