mirror of
https://github.com/logos-messaging/docs.waku.org.git
synced 2026-01-03 13:23:06 +00:00
1 line
18 KiB
JavaScript
1 line
18 KiB
JavaScript
"use strict";(self.webpackChunkwaku_guide=self.webpackChunkwaku_guide||[]).push([[3178],{3905:(e,t,a)=>{a.d(t,{Zo:()=>c,kt:()=>m});var n=a(67294);function s(e,t,a){return t in e?Object.defineProperty(e,t,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[t]=a,e}function r(e,t){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),a.push.apply(a,n)}return a}function i(e){for(var t=1;t<arguments.length;t++){var a=null!=arguments[t]?arguments[t]:{};t%2?r(Object(a),!0).forEach((function(t){s(e,t,a[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(a)):r(Object(a)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(a,t))}))}return e}function o(e,t){if(null==e)return{};var a,n,s=function(e,t){if(null==e)return{};var a,n,s={},r=Object.keys(e);for(n=0;n<r.length;n++)a=r[n],t.indexOf(a)>=0||(s[a]=e[a]);return s}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n<r.length;n++)a=r[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(s[a]=e[a])}return s}var p=n.createContext({}),l=function(e){var t=n.useContext(p),a=t;return e&&(a="function"==typeof e?e(t):i(i({},t),e)),a},c=function(e){var t=l(e.components);return n.createElement(p.Provider,{value:t},e.children)},u="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},g=n.forwardRef((function(e,t){var a=e.components,s=e.mdxType,r=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),u=l(a),g=s,m=u["".concat(p,".").concat(g)]||u[g]||d[g]||r;return a?n.createElement(m,i(i({ref:t},c),{},{components:a})):n.createElement(m,i({ref:t},c))}));function m(e,t){var a=arguments,s=t&&t.mdxType;if("string"==typeof e||s){var r=a.length,i=new Array(r);i[0]=g;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o[u]="string"==typeof e?e:s,i[1]=o;for(var l=2;l<r;l++)i[l]=a[l];return n.createElement.apply(null,i)}return n.createElement.apply(null,a)}g.displayName="MDXCreateElement"},85162:(e,t,a)=>{a.d(t,{Z:()=>i});var n=a(67294),s=a(86010);const r={tabItem:"tabItem_Ymn6"};function i(e){let{children:t,hidden:a,className:i}=e;return n.createElement("div",{role:"tabpanel",className:(0,s.default)(r.tabItem,i),hidden:a},t)}},65488:(e,t,a)=>{a.d(t,{Z:()=>g});var n=a(87462),s=a(67294),r=a(86010),i=a(12466),o=a(70989),p=a(72389);const l={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};function c(e){let{className:t,block:a,selectedValue:o,selectValue:p,tabValues:c}=e;const u=[],{blockElementScrollPositionUntilNextRender:d}=(0,i.o5)(),g=e=>{const t=e.currentTarget,a=u.indexOf(t),n=c[a].value;n!==o&&(d(t),p(n))},m=e=>{let t=null;switch(e.key){case"Enter":g(e);break;case"ArrowRight":{const a=u.indexOf(e.currentTarget)+1;t=u[a]??u[0];break}case"ArrowLeft":{const a=u.indexOf(e.currentTarget)-1;t=u[a]??u[u.length-1];break}}t?.focus()};return s.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.default)("tabs",{"tabs--block":a},t)},c.map((e=>{let{value:t,label:a,attributes:i}=e;return s.createElement("li",(0,n.Z)({role:"tab",tabIndex:o===t?0:-1,"aria-selected":o===t,key:t,ref:e=>u.push(e),onKeyDown:m,onClick:g},i,{className:(0,r.default)("tabs__item",l.tabItem,i?.className,{"tabs__item--active":o===t})}),a??t)})))}function u(e){let{lazy:t,children:a,selectedValue:n}=e;const r=(Array.isArray(a)?a:[a]).filter(Boolean);if(t){const e=r.find((e=>e.props.value===n));return e?(0,s.cloneElement)(e,{className:"margin-top--md"}):null}return s.createElement("div",{className:"margin-top--md"},r.map(((e,t)=>(0,s.cloneElement)(e,{key:t,hidden:e.props.value!==n}))))}function d(e){const t=(0,o.Y)(e);return s.createElement("div",{className:(0,r.default)("tabs-container",l.tabList)},s.createElement(c,(0,n.Z)({},e,t)),s.createElement(u,(0,n.Z)({},e,t)))}function g(e){const t=(0,p.default)();return s.createElement(d,(0,n.Z)({key:String(t)},e))}},68458:(e,t,a)=>{a.r(t),a.d(t,{assets:()=>c,contentTitle:()=>p,default:()=>m,frontMatter:()=>o,metadata:()=>l,toc:()=>u});var n=a(87462),s=(a(67294),a(3905)),r=a(65488),i=a(85162);const o={title:"Build React DApps Using @waku/react",hide_table_of_contents:!0},p=void 0,l={unversionedId:"guides/js-waku/use-waku-react",id:"guides/js-waku/use-waku-react",title:"Build React DApps Using @waku/react",description:"Currently, the JavaScript Waku SDK (@waku/sdk) is NOT compatible with React Native. We plan to add support for React Native in the future.",source:"@site/docs/guides/js-waku/use-waku-react.md",sourceDirName:"guides/js-waku",slug:"/guides/js-waku/use-waku-react",permalink:"/guides/js-waku/use-waku-react",draft:!1,editUrl:"https://github.com/waku-org/docs.waku.org/tree/develop/docs/guides/js-waku/use-waku-react.md",tags:[],version:"current",lastUpdatedAt:1709637863,formattedLastUpdatedAt:"5 Mar 2024",frontMatter:{title:"Build React DApps Using @waku/react",hide_table_of_contents:!0},sidebar:"guides",previous:{title:"Encrypt, Decrypt, and Sign Your Messages",permalink:"/guides/js-waku/message-encryption"},next:{title:"Scaffold DApps Using @waku/create-app",permalink:"/guides/js-waku/use-waku-create-app"}},c={},u=[{value:"Install the dependencies",id:"install-the-dependencies",level:2},{value:"Initialise the Waku provider",id:"initialise-the-waku-provider",level:2},{value:"Build the application interface",id:"build-the-application-interface",level:2},{value:"Send messages using light push",id:"send-messages-using-light-push",level:2},{value:"Receive messages using filter",id:"receive-messages-using-filter",level:2},{value:"Retrieve messages using store",id:"retrieve-messages-using-store",level:2}],d={toc:u},g="wrapper";function m(e){let{components:t,...a}=e;return(0,s.kt)(g,(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,s.kt)("admonition",{type:"caution"},(0,s.kt)("p",{parentName:"admonition"},"Currently, the JavaScript Waku SDK (",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/sdk"),") is ",(0,s.kt)("strong",{parentName:"p"},"NOT compatible")," with React Native. We plan to add support for React Native in the future.")),(0,s.kt)("p",null,"The ",(0,s.kt)("a",{parentName:"p",href:"https://www.npmjs.com/package/@waku/react"},"@waku/react")," package provides components and UI adapters to integrate ",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/sdk")," into React applications effortlessly. This guide provides detailed steps for using ",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/react")," in your project."),(0,s.kt)("h2",{id:"install-the-dependencies"},"Install the dependencies"),(0,s.kt)("p",null,"First, set up a project using any ",(0,s.kt)("a",{parentName:"p",href:"https://react.dev/learn/start-a-new-react-project"},"production-grade React framework")," or an existing React application. For this guide, we will create a boilerplate using ",(0,s.kt)("a",{parentName:"p",href:"https://vitejs.dev/guide/"},"ViteJS"),":"),(0,s.kt)(r.Z,{groupId:"package-manager",mdxType:"Tabs"},(0,s.kt)(i.Z,{value:"npm",label:"NPM",mdxType:"TabItem"},(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-shell"},"npm create vite@latest [PROJECT DIRECTORY] -- --template react\n"))),(0,s.kt)(i.Z,{value:"yarn",label:"Yarn",mdxType:"TabItem"},(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-shell"},"yarn create vite [PROJECT DIRECTORY] --template react\n")))),(0,s.kt)("p",null,"Next, install the required packages for integrating ",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/sdk")," using your preferred package manager:"),(0,s.kt)(r.Z,{groupId:"package-manager",mdxType:"Tabs"},(0,s.kt)(i.Z,{value:"npm",label:"NPM",mdxType:"TabItem"},(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-shell"},"npm install @waku/react @waku/sdk protobufjs\n"))),(0,s.kt)(i.Z,{value:"yarn",label:"Yarn",mdxType:"TabItem"},(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-shell"},"yarn add @waku/react @waku/sdk protobufjs\n")))),(0,s.kt)("h2",{id:"initialise-the-waku-provider"},"Initialise the Waku provider"),(0,s.kt)("p",null,"In the ",(0,s.kt)("inlineCode",{parentName:"p"},"main.jsx")," file, which serves as the entry point for a React app, we will set up the ",(0,s.kt)("inlineCode",{parentName:"p"},"LightNodeProvider")," ",(0,s.kt)("a",{parentName:"p",href:"https://react.dev/reference/react/createContext#provider"},"context provider")," to wrap the entire application within the Waku provider. Import the following on top of your file:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/main.jsx"',title:'"src/main.jsx"'},"import { LightNodeProvider } from \"@waku/react\";\n\n// Set the Light Node options\nconst NODE_OPTIONS = { defaultBootstrap: true };\n\nReactDOM.createRoot(document.getElementById('root')).render(\n // Use the Light Node context provider\n <React.StrictMode>\n <LightNodeProvider options={NODE_OPTIONS}>\n <App />\n </LightNodeProvider>\n </React.StrictMode>,\n)\n")),(0,s.kt)("p",null,"Next, create and start a ",(0,s.kt)("a",{parentName:"p",href:"/learn/glossary#light-node"},"Light Node")," using the ",(0,s.kt)("inlineCode",{parentName:"p"},"useWaku()")," function within the ",(0,s.kt)("inlineCode",{parentName:"p"},"App.jsx")," file:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/App.jsx"',title:'"src/App.jsx"'},'import { useWaku } from "@waku/react";\n\nfunction App() {\n // Create and start a Light Node\n const { node, error, isLoading } = useWaku();\n\n // "node" is the created Light Node\n // "error" captures any error that occurs during node creation\n // "isLoading" indicates whether the node is still being created\n}\n')),(0,s.kt)("h2",{id:"build-the-application-interface"},"Build the application interface"),(0,s.kt)("p",null,"Let's build a user interface for sending messages and viewing past messages, modify the ",(0,s.kt)("inlineCode",{parentName:"p"},"App.jsx")," file with the following code block:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/App.jsx"',title:'"src/App.jsx"'},'import { useState, useEffect } from \'react\';\nimport { useWaku } from "@waku/react";\nimport { createEncoder, createDecoder } from "@waku/sdk";\nimport protobuf from \'protobufjs\';\nimport \'./App.css\'\n\nfunction App() {\n const [inputMessage, setInputMessage] = useState("");\n const [messages, setMessages] = useState([]);\n\n // Update the inputMessage state as the user input changes\n const handleInputChange = (e) => {\n setInputMessage(e.target.value);\n };\n\n // Create and start a Light Node\n const { node, error, isLoading } = useWaku();\n\n // Create a message encoder and decoder\n const contentTopic = "/waku-react-guide/1/chat/proto";\n const encoder = createEncoder({ contentTopic });\n const decoder = createDecoder(contentTopic);\n\n // Create a message structure using Protobuf\n const ChatMessage = new protobuf.Type("ChatMessage")\n .add(new protobuf.Field("timestamp", 1, "uint64"))\n .add(new protobuf.Field("message", 2, "string"));\n\n // Send the message using Light Push\n const sendMessage = async () => {}\n\n return (\n <>\n <div className="chat-interface">\n <h1>Waku React Demo</h1>\n <div className="chat-body">\n {messages.map((message, index) => (\n <div key={index} className="chat-message">\n <span>{new Date(message.timestamp).toUTCString()}</span>\n <div className="message-text">{message.message}</div>\n </div>\n ))}\n </div>\n <div className="chat-footer">\n <input\n type="text"\n id="message-input"\n value={inputMessage}\n onChange={handleInputChange}\n placeholder="Type your message..."\n />\n <button className="send-button" onClick={sendMessage}>Send</button>\n </div>\n </div>\n </>\n )\n}\n\nexport default App\n')),(0,s.kt)("admonition",{type:"info"},(0,s.kt)("p",{parentName:"admonition"},"In the code above, we also created a message ",(0,s.kt)("inlineCode",{parentName:"p"},"encoder")," and ",(0,s.kt)("inlineCode",{parentName:"p"},"decoder")," using the ",(0,s.kt)("inlineCode",{parentName:"p"},"createEncoder()")," and ",(0,s.kt)("inlineCode",{parentName:"p"},"createDecoder()")," functions, along with the application ",(0,s.kt)("a",{parentName:"p",href:"/guides/js-waku/#message-structure"},"message structure")," with Protobuf.")),(0,s.kt)("p",null,"Next, modify the ",(0,s.kt)("inlineCode",{parentName:"p"},"App.css")," file with the following code block:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-css",metastring:'title="src/App.css"',title:'"src/App.css"'},"#root {\n margin: 0 auto;\n}\n\n.chat-interface {\n display: flex;\n flex-direction: column;\n height: 100vh;\n border: 1px solid #ccc;\n}\n\n.chat-body {\n flex-grow: 1;\n overflow-y: auto;\n padding: 10px;\n}\n\n.message-text {\n background-color: #f1f1f1;\n color: #000;\n padding: 10px;\n margin-bottom: 10px;\n}\n\n.chat-footer {\n display: flex;\n padding: 10px;\n background-color: #f1f1f1;\n align-items: center;\n}\n\n#message-input {\n flex-grow: 1;\n border-radius: 4px;\n padding: 10px;\n margin-right: 10px;\n}\n\n.send-button {\n background-color: #007bff;\n border-radius: 4px;\n}\n")),(0,s.kt)("h2",{id:"send-messages-using-light-push"},"Send messages using light push"),(0,s.kt)("p",null,"To send messages in our application, we need to modify the ",(0,s.kt)("inlineCode",{parentName:"p"},"sendMessage()")," function to serialize user input into our Protobuf structure and ",(0,s.kt)("a",{parentName:"p",href:"/guides/js-waku/light-send-receive#send-messages-using-light-push"},"push it to the network")," using the ",(0,s.kt)("inlineCode",{parentName:"p"},"useLightPush()")," function:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/App.jsx"',title:'"src/App.jsx"'},'import { useLightPush } from "@waku/react";\n\nfunction App() {\n // Bind push method to a node and encoder\n const { push } = useLightPush({ node, encoder });\n\n // Send the message using Light Push\n const sendMessage = async () => {\n if (!push || inputMessage.length === 0) return;\n\n // Create a new message object\n const timestamp = Date.now();\n const protoMessage = ChatMessage.create({\n timestamp: timestamp,\n message: inputMessage\n });\n\n // Serialise the message and push to the network\n const payload = ChatMessage.encode(protoMessage).finish();\n const { recipients, errors } = await push({ payload, timestamp });\n\n // Check for errors\n if (errors.length === 0) {\n setInputMessage("");\n console.log("MESSAGE PUSHED");\n } else {\n console.log(errors);\n }\n };\n}\n')),(0,s.kt)("h2",{id:"receive-messages-using-filter"},"Receive messages using filter"),(0,s.kt)("p",null,"To display messages in our application, we need to use the ",(0,s.kt)("inlineCode",{parentName:"p"},"useFilterMessages()")," function to create a ",(0,s.kt)("a",{parentName:"p",href:"/guides/js-waku/light-send-receive/#receive-messages-using-filter"},"Filter subscription"),", receive incoming messages, and render them in our interface:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/App.jsx"',title:'"src/App.jsx"'},'import { useFilterMessages } from "@waku/react";\n\nfunction App() {\n // Receive messages from Filter subscription\n const { messages: filterMessages } = useFilterMessages({ node, decoder });\n\n // Render the list of messages\n useEffect(() => {\n setMessages(filterMessages.map((wakuMessage) => {\n if (!wakuMessage.payload) return;\n return ChatMessage.decode(wakuMessage.payload);\n }));\n }, [filterMessages]);\n}\n')),(0,s.kt)("h2",{id:"retrieve-messages-using-store"},"Retrieve messages using store"),(0,s.kt)("p",null,"To display messages from the past, we need to retrieve them from the ",(0,s.kt)("a",{parentName:"p",href:"/guides/js-waku/store-retrieve-messages"},"Store protocol")," using the ",(0,s.kt)("inlineCode",{parentName:"p"},"useStoreMessages()")," function when our application initialises and then render them alongside newly received messages:"),(0,s.kt)("pre",null,(0,s.kt)("code",{parentName:"pre",className:"language-js",metastring:'title="src/App.jsx"',title:'"src/App.jsx"'},'import { useFilterMessages, useStoreMessages } from "@waku/react";\n\nfunction App() {\n // Query Store peers for past messages\n const { messages: storeMessages } = useStoreMessages({ node, decoder });\n\n // Receive messages from Filter subscription\n const { messages: filterMessages } = useFilterMessages({ node, decoder });\n\n // Render both past and new messages\n useEffect(() => {\n const allMessages = storeMessages.concat(filterMessages);\n setMessages(allMessages.map((wakuMessage) => {\n if (!wakuMessage.payload) return;\n return ChatMessage.decode(wakuMessage.payload);\n }));\n }, [filterMessages, storeMessages]);\n}\n')),(0,s.kt)("admonition",{type:"info"},(0,s.kt)("p",{parentName:"admonition"},"To explore the available Store query options, have a look at the ",(0,s.kt)("a",{parentName:"p",href:"/guides/js-waku/store-retrieve-messages#store-query-options"},"Retrieve Messages Using Store Protocol")," guide.")),(0,s.kt)("admonition",{type:"tip"},(0,s.kt)("p",{parentName:"admonition"},"You have successfully integrated ",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/sdk")," into a React application using the ",(0,s.kt)("inlineCode",{parentName:"p"},"@waku/react")," package. Have a look at the ",(0,s.kt)("a",{parentName:"p",href:"https://github.com/waku-org/js-waku-examples/tree/master/examples/web-chat"},"web-chat")," example for a working demo and the ",(0,s.kt)("a",{parentName:"p",href:"https://blog.waku.org/tictactoe-tutorial"},"Building a Tic-Tac-Toe Game with Waku")," tutorial to learn more.")))}m.isMDXComponent=!0}}]); |