mirror of
https://github.com/logos-messaging/docs.waku.org.git
synced 2026-01-05 06:13:08 +00:00
1 line
17 KiB
JavaScript
1 line
17 KiB
JavaScript
"use strict";(self.webpackChunkwaku_guide=self.webpackChunkwaku_guide||[]).push([[6344],{3502:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>d,default:()=>h,frontMatter:()=>c,metadata:()=>s,toc:()=>u});const s=JSON.parse('{"id":"build/javascript/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/build/javascript/use-waku-react.md","sourceDirName":"build/javascript","slug":"/build/javascript/use-waku-react","permalink":"/build/javascript/use-waku-react","draft":false,"unlisted":false,"editUrl":"https://github.com/waku-org/docs.waku.org/tree/develop/docs/build/javascript/use-waku-react.md","tags":[],"version":"current","lastUpdatedAt":null,"frontMatter":{"title":"Build React DApps Using @waku/react","hide_table_of_contents":true,"displayed_sidebar":"build"},"sidebar":"build","previous":{"title":"Encrypt, Decrypt, and Sign Your Messages","permalink":"/build/javascript/message-encryption"},"next":{"title":"Scaffold DApps Using @waku/create-app","permalink":"/build/javascript/use-waku-create-app"}}');var a=n(74848),r=n(28453),i=n(4865),o=n(19365);const c={title:"Build React DApps Using @waku/react",hide_table_of_contents:!0,displayed_sidebar:"build"},d=void 0,l={},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}];function p(e){const t={a:"a",admonition:"admonition",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t.admonition,{type:"caution",children:(0,a.jsxs)(t.p,{children:["Currently, the JavaScript Waku SDK (",(0,a.jsx)(t.code,{children:"@waku/sdk"}),") is ",(0,a.jsx)(t.strong,{children:"NOT compatible"})," with React Native. We plan to add support for React Native in the future."]})}),"\n",(0,a.jsxs)(t.p,{children:["The ",(0,a.jsx)(t.a,{href:"https://www.npmjs.com/package/@waku/react",children:"@waku/react"})," package provides components and UI adapters to integrate ",(0,a.jsx)(t.code,{children:"@waku/sdk"})," into React applications effortlessly. This guide provides detailed steps for using ",(0,a.jsx)(t.code,{children:"@waku/react"})," in your project."]}),"\n",(0,a.jsx)(t.h2,{id:"install-the-dependencies",children:"Install the dependencies"}),"\n",(0,a.jsxs)(t.p,{children:["First, set up a project using any ",(0,a.jsx)(t.a,{href:"https://react.dev/learn/start-a-new-react-project",children:"production-grade React framework"})," or an existing React application. For this guide, we will create a boilerplate using ",(0,a.jsx)(t.a,{href:"https://vitejs.dev/guide/",children:"ViteJS"}),":"]}),"\n","\n",(0,a.jsxs)(i.A,{groupId:"package-manager",children:[(0,a.jsx)(o.A,{value:"npm",label:"NPM",children:(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-shell",children:"npm create vite@latest [PROJECT DIRECTORY] -- --template react\n"})})}),(0,a.jsx)(o.A,{value:"yarn",label:"Yarn",children:(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-shell",children:"yarn create vite [PROJECT DIRECTORY] --template react\n"})})})]}),"\n",(0,a.jsxs)(t.p,{children:["Next, install the required packages for integrating ",(0,a.jsx)(t.code,{children:"@waku/sdk"})," using your preferred package manager:"]}),"\n",(0,a.jsxs)(i.A,{groupId:"package-manager",children:[(0,a.jsx)(o.A,{value:"npm",label:"NPM",children:(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-shell",children:"npm install @waku/react @waku/sdk protobufjs\n"})})}),(0,a.jsx)(o.A,{value:"yarn",label:"Yarn",children:(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-shell",children:"yarn add @waku/react @waku/sdk protobufjs\n"})})})]}),"\n",(0,a.jsx)(t.h2,{id:"initialise-the-waku-provider",children:"Initialise the Waku provider"}),"\n",(0,a.jsxs)(t.p,{children:["In the ",(0,a.jsx)(t.code,{children:"main.jsx"})," file, which serves as the entry point for a React app, we will set up the ",(0,a.jsx)(t.code,{children:"LightNodeProvider"})," ",(0,a.jsx)(t.a,{href:"https://react.dev/reference/react/createContext#provider",children:"context provider"})," to wrap the entire application within the Waku provider. Import the following on top of your file:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/main.jsx"',children:"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"})}),"\n",(0,a.jsxs)(t.p,{children:["Next, create and start a ",(0,a.jsx)(t.a,{href:"/learn/glossary#light-node",children:"Light Node"})," using the ",(0,a.jsx)(t.code,{children:"useWaku()"})," function within the ",(0,a.jsx)(t.code,{children:"App.jsx"})," file:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/App.jsx"',children:'import { useWaku } from "@waku/react";\n\nfunction App() {\n\t// Create and start a Light Node\n\tconst { node, error, isLoading } = useWaku();\n\n\t// "node" is the created Light Node\n\t// "error" captures any error that occurs during node creation\n\t// "isLoading" indicates whether the node is still being created\n}\n'})}),"\n",(0,a.jsx)(t.h2,{id:"build-the-application-interface",children:"Build the application interface"}),"\n",(0,a.jsxs)(t.p,{children:["Let's build a user interface for sending messages and viewing past messages, modify the ",(0,a.jsx)(t.code,{children:"App.jsx"})," file with the following code block:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/App.jsx"',children:'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\tconst [inputMessage, setInputMessage] = useState("");\n\tconst [messages, setMessages] = useState([]);\n\n\t// Update the inputMessage state as the user input changes\n\tconst handleInputChange = (e) => {\n\t\tsetInputMessage(e.target.value);\n\t};\n\n\t// Create and start a Light Node\n\tconst { node, error, isLoading } = useWaku();\n\n\t// Create a message encoder and decoder\n\tconst contentTopic = "/waku-react-guide/1/chat/proto";\n\tconst encoder = createEncoder({ contentTopic });\n\tconst decoder = createDecoder(contentTopic);\n\n\t// Create a message structure using Protobuf\n\tconst DataPacket = new protobuf.Type("DataPacket")\n\t\t.add(new protobuf.Field("timestamp", 1, "uint64"))\n\t\t.add(new protobuf.Field("message", 2, "string"));\n\n\t// Send the message using Light Push\n\tconst sendMessage = async () => {}\n\n\treturn (\n\t\t<>\n\t\t\t<div className="chat-interface">\n\t\t\t\t<h1>Waku React Demo</h1>\n\t\t\t\t<div className="chat-body">\n\t\t\t\t\t{messages.map((message, index) => (\n\t\t\t\t\t\t<div key={index} className="chat-message">\n\t\t\t\t\t\t\t<span>{new Date(message.timestamp).toUTCString()}</span>\n\t\t\t\t\t\t\t<div className="message-text">{message.message}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t\t<div className="chat-footer">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype="text"\n\t\t\t\t\t\tid="message-input"\n\t\t\t\t\t\tvalue={inputMessage}\n\t\t\t\t\t\tonChange={handleInputChange}\n\t\t\t\t\t\tplaceholder="Type your message..."\n\t\t\t\t\t/>\n\t\t\t\t\t<button className="send-button" onClick={sendMessage}>Send</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</>\n\t)\n}\n\nexport default App\n'})}),"\n",(0,a.jsx)(t.admonition,{type:"info",children:(0,a.jsxs)(t.p,{children:["In the code above, we also created a message ",(0,a.jsx)(t.code,{children:"encoder"})," and ",(0,a.jsx)(t.code,{children:"decoder"})," using the ",(0,a.jsx)(t.code,{children:"createEncoder()"})," and ",(0,a.jsx)(t.code,{children:"createDecoder()"})," functions, along with the application ",(0,a.jsx)(t.a,{href:"/build/javascript/#message-structure",children:"message structure"})," with Protobuf."]})}),"\n",(0,a.jsxs)(t.p,{children:["Next, modify the ",(0,a.jsx)(t.code,{children:"App.css"})," file with the following code block:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-css",metastring:'title="src/App.css"',children:"#root {\n\tmargin: 0 auto;\n}\n\n.chat-interface {\n\tdisplay: flex;\n\tflex-direction: column;\n\theight: 100vh;\n\tborder: 1px solid #ccc;\n}\n\n.chat-body {\n\tflex-grow: 1;\n\toverflow-y: auto;\n\tpadding: 10px;\n}\n\n.message-text {\n\tbackground-color: #f1f1f1;\n\tcolor: #000;\n\tpadding: 10px;\n\tmargin-bottom: 10px;\n}\n\n.chat-footer {\n\tdisplay: flex;\n\tpadding: 10px;\n\tbackground-color: #f1f1f1;\n\talign-items: center;\n}\n\n#message-input {\n\tflex-grow: 1;\n\tborder-radius: 4px;\n\tpadding: 10px;\n\tmargin-right: 10px;\n}\n\n.send-button {\n\tbackground-color: #007bff;\n\tborder-radius: 4px;\n}\n"})}),"\n",(0,a.jsx)(t.h2,{id:"send-messages-using-light-push",children:"Send messages using light push"}),"\n",(0,a.jsxs)(t.p,{children:["To send messages in our application, we need to modify the ",(0,a.jsx)(t.code,{children:"sendMessage()"})," function to serialize user input into our Protobuf structure and ",(0,a.jsx)(t.a,{href:"/build/javascript/light-send-receive#send-messages-using-light-push",children:"push it to the network"})," using the ",(0,a.jsx)(t.code,{children:"useLightPush()"})," function:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/App.jsx"',children:'import { useLightPush } from "@waku/react";\n\nfunction App() {\n\t// Bind push method to a node and encoder\n\tconst { push } = useLightPush({ node, encoder });\n\n\t// Send the message using Light Push\n\tconst sendMessage = async () => {\n\t\tif (!push || inputMessage.length === 0) return;\n\n\t\t// Create a new message object\n\t\tconst timestamp = Date.now();\n\t\tconst protoMessage = DataPacket.create({\n\t\t\ttimestamp: timestamp,\n\t\t\tmessage: inputMessage\n\t\t});\n\n\t\t// Serialise the message and push to the network\n\t\tconst payload = DataPacket.encode(protoMessage).finish();\n\t\tconst { recipients, errors } = await push({ payload, timestamp });\n\n\t\t// Check for errors\n\t\tif (errors.length === 0) {\n\t\t\tsetInputMessage("");\n\t\t\tconsole.log("MESSAGE PUSHED");\n\t\t} else {\n\t\t\tconsole.log(errors);\n\t\t}\n\t};\n}\n'})}),"\n",(0,a.jsx)(t.h2,{id:"receive-messages-using-filter",children:"Receive messages using filter"}),"\n",(0,a.jsxs)(t.p,{children:["To display messages in our application, we need to use the ",(0,a.jsx)(t.code,{children:"useFilterMessages()"})," function to create a ",(0,a.jsx)(t.a,{href:"/build/javascript/light-send-receive/#receive-messages-using-filter",children:"Filter subscription"}),", receive incoming messages, and render them in our interface:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/App.jsx"',children:'import { useFilterMessages } from "@waku/react";\n\nfunction App() {\n\t// Receive messages from Filter subscription\n\tconst { messages: filterMessages } = useFilterMessages({ node, decoder });\n\n\t// Render the list of messages\n\tuseEffect(() => {\n\t\tsetMessages(filterMessages.map((wakuMessage) => {\n\t\t\tif (!wakuMessage.payload) return;\n\t\t\treturn DataPacket.decode(wakuMessage.payload);\n\t\t}));\n\t}, [filterMessages]);\n}\n'})}),"\n",(0,a.jsx)(t.h2,{id:"retrieve-messages-using-store",children:"Retrieve messages using store"}),"\n",(0,a.jsxs)(t.p,{children:["To display messages from the past, we need to retrieve them from the ",(0,a.jsx)(t.a,{href:"/build/javascript/store-retrieve-messages",children:"Store protocol"})," using the ",(0,a.jsx)(t.code,{children:"useStoreMessages()"})," function when our application initialises and then render them alongside newly received messages:"]}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",metastring:'title="src/App.jsx"',children:'import { useFilterMessages, useStoreMessages } from "@waku/react";\n\nfunction App() {\n\t// Query Store peers for past messages\n\tconst { messages: storeMessages } = useStoreMessages({ node, decoder });\n\n\t// Receive messages from Filter subscription\n\tconst { messages: filterMessages } = useFilterMessages({ node, decoder });\n\n\t// Render both past and new messages\n\tuseEffect(() => {\n\t\tconst allMessages = storeMessages.concat(filterMessages);\n\t\tsetMessages(allMessages.map((wakuMessage) => {\n\t\t\tif (!wakuMessage.payload) return;\n\t\t\treturn DataPacket.decode(wakuMessage.payload);\n\t\t}));\n\t}, [filterMessages, storeMessages]);\n}\n'})}),"\n",(0,a.jsx)(t.p,{children:"You can also configure a specific Store peer when creating the node, which is useful when running your own Store node or using a specific node in the network:"}),"\n",(0,a.jsx)(t.pre,{children:(0,a.jsx)(t.code,{className:"language-js",children:'const node = await createLightNode({ \n defaultBootstrap: true,\n store: {\n peer: "/ip4/1.2.3.4/tcp/1234/p2p/16Uiu2HAm..." // multiaddr or PeerId of your Store node\n }\n});\n'})}),"\n",(0,a.jsx)(t.p,{children:"If the specified Store peer is not available, the node will fall back to using random Store peers in the network."}),"\n",(0,a.jsx)(t.admonition,{type:"info",children:(0,a.jsxs)(t.p,{children:["To explore the available Store query options, have a look at the ",(0,a.jsx)(t.a,{href:"/build/javascript/store-retrieve-messages#store-query-options",children:"Retrieve Messages Using Store Protocol"})," guide."]})}),"\n",(0,a.jsx)(t.admonition,{type:"tip",children:(0,a.jsxs)(t.p,{children:["You have successfully integrated ",(0,a.jsx)(t.code,{children:"@waku/sdk"})," into a React application using the ",(0,a.jsx)(t.code,{children:"@waku/react"})," package. Have a look at the ",(0,a.jsx)(t.a,{href:"https://github.com/waku-org/js-waku-examples/tree/master/examples/web-chat",children:"web-chat"})," example for a working demo and the ",(0,a.jsx)(t.a,{href:"https://blog.waku.org/2024-01-22-tictactoe-tutorial/",children:"Building a Tic-Tac-Toe Game with Waku"})," tutorial to learn more."]})})]})}function h(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,a.jsx)(t,{...e,children:(0,a.jsx)(p,{...e})}):p(e)}},4865:(e,t,n)=>{n.d(t,{A:()=>h});var s=n(96540),a=n(34164),r=n(23104),i=n(47751),o=n(92303);const c={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var d=n(74848);function l({className:e,block:t,selectedValue:n,selectValue:s,tabValues:i}){const o=[],{blockElementScrollPositionUntilNextRender:l}=(0,r.a_)(),u=e=>{const t=e.currentTarget,a=o.indexOf(t),r=i[a].value;r!==n&&(l(t),s(r))},p=e=>{let t=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const n=o.indexOf(e.currentTarget)+1;t=o[n]??o[0];break}case"ArrowLeft":{const n=o.indexOf(e.currentTarget)-1;t=o[n]??o[o.length-1];break}}t?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":t},e),children:i.map(({value:e,label:t,attributes:s})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{o.push(e)},onKeyDown:p,onClick:u,...s,className:(0,a.A)("tabs__item",c.tabItem,s?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function u({lazy:e,children:t,selectedValue:n}){const r=(Array.isArray(t)?t:[t]).filter(Boolean);if(e){const e=r.find(e=>e.props.value===n);return e?(0,s.cloneElement)(e,{className:(0,a.A)("margin-top--md",e.props.className)}):null}return(0,d.jsx)("div",{className:"margin-top--md",children:r.map((e,t)=>(0,s.cloneElement)(e,{key:t,hidden:e.props.value!==n}))})}function p(e){const t=(0,i.u)(e);return(0,d.jsxs)("div",{className:(0,a.A)("tabs-container",c.tabList),children:[(0,d.jsx)(l,{...t,...e}),(0,d.jsx)(u,{...t,...e})]})}function h(e){const t=(0,o.default)();return(0,d.jsx)(p,{...e,children:(0,i.v)(e.children)},String(t))}},19365:(e,t,n)=>{n.d(t,{A:()=>i});n(96540);var s=n(34164);const a={tabItem:"tabItem_Ymn6"};var r=n(74848);function i({children:e,hidden:t,className:n}){return(0,r.jsx)("div",{role:"tabpanel",className:(0,s.A)(a.tabItem,n),hidden:t,children:e})}},28453:(e,t,n)=>{n.d(t,{R:()=>i,x:()=>o});var s=n(96540);const a={},r=s.createContext(a);function i(e){const t=s.useContext(r);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),s.createElement(r.Provider,{value:t},e.children)}}}]); |