CMS Development

amjadg
Membre

Embed Chat Into A ReactJS SPA

Résolue

So, is there an embed pattern to embed/integrate the chatbot into my reactJS SPA ? I couldn'y find an example or even an npm module for the chatbot. Is there a clean integration pattern for this into react ? Please share details.

1 Solution acceptée
kierana
Solution
Contributeur

Embed Chat Into A ReactJS SPA

Résolue

Something like this should work:

 

// Create a hook to seperate out logic.
const useHubspotChat = (portalId) => { const [ hasLoaded, setHasLoaded ] = React.useState(false); const [ activeConversation, setActiveConversation ] = React.useState(false); const eventRef = React.useRef(null); React.useEffect(() => { console.log('hey') // Add event listener. window.hsConversationsOnReady = [() => { setHasLoaded(true); }]; // Create script component. let script = document.createElement('script'); script.src=`//js.hs-scripts.com/${portalId}.js`; script.async = true; document.body.appendChild(script); return () => { document.body.removeChild(script); window.hsConversationsOnReady = []; } }, []); // Subscripe to conversation events. React.useEffect(() => { eventRef.current = (payload) => { setActiveConversation(payload.conversation.conversationId); } if (hasLoaded) { window.HubSpotConversations.on( 'conversationStarted', eventRef.current); } return () => { window.HubSpotConversations.off('conversationStarted', eventRef.current); } }, [hasLoaded]); const openHandler = React.useCallback(() => { if (hasLoaded) { window.HubSpotConversations.widget.open(); } }, [hasLoaded]); const closeHandler = React.useCallback(() => { if (hasLoaded) { window.HubSpotConversations.widget.close(); } }, [hasLoaded]) return { hasLoaded, activeConversation, openHandler, closeHandler }; } const App = () => { const { hasLoaded, activeConversation, openHandler, closeHandler } = useHubspotChat(<portalidhere>); return ( <div> hi {hasLoaded ? 'yes': 'no'} conversation: { activeConversation } <button onClick={openHandler}>click me to open</button> <button onClick={closeHandler}>click me to close</button> </div>) } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

 

Voir la solution dans l'envoi d'origine

13 Réponses
DLemesh
Membre

Embed Chat Into A ReactJS SPA

Résolue

Just wanted to post my take on it.
I needed the ability to control the widget via a custom button in my app.

I created a provider for the widget and used CSS to show/hide the widget.

 

First and most important, is to make sure the widget won't load initally, so in the index.html you sould add this snippet at the end of the <body> tag:

<!-- Start of HubSpot Embed Code -->
<script>
window.hsConversationsSettings = {
loadImmediately: false,
inlineEmbedSelector: '#hubspot-conversations-inline-embed-selector',
};
</script>
<script type="text/javascript" id="hs-script-loader" async defer src="//js-na1.hs-scripts.com/your-portal-id-here.js"></script>
<!-- End of HubSpot Embed Code -->


This is the provider: HubspotConversationsProvider.tsx

import { createStyles } from '@mantine/core';
import React, {
createContext,
FC,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import ReactDOM from 'react-dom';
import { useLocation } from 'react-router-dom';

interface HubspotConversationsContextType {
toggleWidget: () => void;
isOpen: boolean;
unreadMessagesCount: number;
}

const HubspotConversationsContext =
createContext<HubspotConversationsContextType | null>(null);

const HUBSPOT_INLINE_EMBED_ELEMENT_ID =
'hubspot-conversations-inline-embed-selector';

export const HubspotConversationsProvider: FC = ({ children }) => {
const location = useLocation();

const [isReady, setIsReady] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const [unreadMessagesCount, setUnreadMessagesCount] = useState(0);

const { classes } = useStyles(isOpen);

const user = useCurrentUser();
// This is for Visitor Identification API
const hubspotToken = useHubspotConversationsToken();

const hideWidget = useCallback(() => {
setIsOpen(false);
}, []);

const showWidget = useCallback(() => {
if (!isReady) return;

window.HubSpotConversations.widget.load();
window.HubSpotConversations.widget.open();

setIsOpen(true);
}, [isReady]);

const toggleWidget = useCallback(() => {
if (isOpen) {
hideWidget();
} else {
showWidget();
}
}, [hideWidget, isOpen, showWidget]);

const onConversationsReady = useCallback(() => {
setIsReady(true);
}, []);

useEffect(
function init() {
if (window.HubSpotConversations) {
onConversationsReady();
} else {
window.hsConversationsOnReady = [onConversationsReady];
}
},
[onConversationsReady]
);

useEffect(
function updateSettingsAndLoadWidget() {
if (hubspotToken.isFetched && hubspotToken?.data) {
window.hsConversationsSettings = {
...window.hsConversationsSettings,
identificationEmail: user?.email,
identificationToken: hubspotToken.data.token,
};
window.HubSpotConversations.widget.load();
}
},
[hubspotToken.data, hubspotToken.isFetched, user?.email]
);

useEffect(
function addEventListeners() {
if (!isReady) return;

function listener(payload: { unreadCount: number }) {
setUnreadMessagesCount(payload.unreadCount);
}

window.HubSpotConversations.on(
'unreadConversationCountChanged',
listener
);

return () => {
window.HubSpotConversations.off(
'unreadConversationCountChanged',
listener
);
};
},
[isReady]
);

useEffect(
function refreshConversationsOnRouteChange() {
if (!isReady) return;

window.HubSpotConversations.widget.refresh();
},
[isReady]
);

return (
<HubspotConversationsContext.Provider
value={{ isOpen, toggleWidget, unreadMessagesCount }}
>
{children}

{ReactDOM.createPortal(
<div
className={classes.chatWidgetContainer}
id={HUBSPOT_INLINE_EMBED_ELEMENT_ID}
/>,
document.body
)}
</HubspotConversationsContext.Provider>
);
};

const useStyles = createStyles((theme, isOpen: boolean) => ({
chatWidgetContainer: {
overflow: 'hidden',
zIndex: 2147483647, // this is the max possible value
position: 'fixed',
top: 75,
right: theme.spacing.xl,
height: 500,
width: 376,
display: isOpen ? 'block' : 'none',
borderRadius: theme.radius.md,
backgroundColor: theme.white,
boxShadow: '0 5px 20px rgb(0 0 0 / 10%)',

'#hubspot-conversations-inline-parent': {
width: '100%',
height: '100%',
},

'#hubspot-conversations-inline-iframe': {
width: '100%',
height: '100%',
border: 'none',
},
},
}));

export function useHubspotConversations() {
const context = useContext(HubspotConversationsContext);

if (context === null) {
throw new Error(
'useHubspotConversations must be used within HubspotConversationsProvider'
);
}

return context;
}

declare global {
interface Window {
hsConversationsSettings: Record<string, any>;
hsConversationsOnReady: Array<() => void>;
HubSpotConversations: {
on: any;
off: any;
widget: {
status: () => { loaded: boolean; pending: boolean };
load: (params?: { widgetOpen: boolean }) => void;
remove: () => void;
open: () => void;
close: () => void;
refresh: (openToNewThread?: boolean) => void;
};
};
}
}

 

And the usage can be something like that:

function App() {
return (
<HubspotConversationsProvider>
<ChatWidgetButton />
</HubspotConversationsProvider>
);
}

function ChatWidgetButton() {
const { toggleWidget } = useHubspotConversations();

return <button onClick={toggleWidget}>Toggle chat</button>;
}

 

0 Votes
JIanniciello
Membre

Embed Chat Into A ReactJS SPA

Résolue

Jquery question here: 
Does anyone know if it is possible to embed a chatbot into a web app written in Jquery? Thanks!

0 Votes
DA3
Participant

Embed Chat Into A ReactJS SPA

Résolue
class App extends Component {

    componentDidMount() {

        const script = document.createElement("script");

        script.src="//js.hs-scripts.com/{}.js";
        script.async = true;

        document.body.appendChild(script);

    }

    render() {

        return (
            <>
                <AppLayout/>
                <Cookiebar/>
            </>
        );
    }
}
Mark_Ryba
Contributeur | Partenaire solutions Elite
Contributeur | Partenaire solutions Elite

Embed Chat Into A ReactJS SPA

Résolue

It should work, but it may depend on how you're handling routing. Basically the way the chat function works is you add the standard HS scripts and then configure (in HubSpot) where and when the live chat should pop up. Basically the script needs to be available for the whole app, and then the triggers are handled on HubSpot's side instead of in your codebase. I'm not aware of an NPM or webpack-friendly method of bundling that script, however. I've always done it manually.

 

There may be a conflict between React versions since HubSpot uses React for a lot of their on-page tools. That shouldn't happen, but it is something I've run into in the past. React Dev Tools might help diagnose if there's something like that going on.

kierana
Contributeur

Embed Chat Into A ReactJS SPA

Résolue

I guess if you're using React Router - or some other router you can simply invoke window.HubSpotConversations.widget.refresh(); which apparently re-runs the page matching logic based on the current URL.

kierana
Solution
Contributeur

Embed Chat Into A ReactJS SPA

Résolue

Something like this should work:

 

// Create a hook to seperate out logic.
const useHubspotChat = (portalId) => { const [ hasLoaded, setHasLoaded ] = React.useState(false); const [ activeConversation, setActiveConversation ] = React.useState(false); const eventRef = React.useRef(null); React.useEffect(() => { console.log('hey') // Add event listener. window.hsConversationsOnReady = [() => { setHasLoaded(true); }]; // Create script component. let script = document.createElement('script'); script.src=`//js.hs-scripts.com/${portalId}.js`; script.async = true; document.body.appendChild(script); return () => { document.body.removeChild(script); window.hsConversationsOnReady = []; } }, []); // Subscripe to conversation events. React.useEffect(() => { eventRef.current = (payload) => { setActiveConversation(payload.conversation.conversationId); } if (hasLoaded) { window.HubSpotConversations.on( 'conversationStarted', eventRef.current); } return () => { window.HubSpotConversations.off('conversationStarted', eventRef.current); } }, [hasLoaded]); const openHandler = React.useCallback(() => { if (hasLoaded) { window.HubSpotConversations.widget.open(); } }, [hasLoaded]); const closeHandler = React.useCallback(() => { if (hasLoaded) { window.HubSpotConversations.widget.close(); } }, [hasLoaded]) return { hasLoaded, activeConversation, openHandler, closeHandler }; } const App = () => { const { hasLoaded, activeConversation, openHandler, closeHandler } = useHubspotChat(<portalidhere>); return ( <div> hi {hasLoaded ? 'yes': 'no'} conversation: { activeConversation } <button onClick={openHandler}>click me to open</button> <button onClick={closeHandler}>click me to close</button> </div>) } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

 

RScotten
Participant

Embed Chat Into A ReactJS SPA

Résolue

Learning about this was super helpful:

window.HubSpotConversations.widget...

 

lscanlan
Ancien salarié HubSpot
Ancien salarié HubSpot

Embed Chat Into A ReactJS SPA

Résolue

Hi @amjadg,

 

I haven't done this myself but I've been doing some searching around our forums. Here's an example from a while back where someone added the tracking code to a React app: https://community.hubspot.com/t5/APIs-Integrations/Tracking-code-for-a-single-page-application-made-.... If you look at the bottom response there, the user built a page listener component. Maybe that would help with what you're trying to do?

 

I also found this: https://community.hubspot.com/t5/CMS-Development/Chat-integration-issue-with-React/m-p/275219 while doing a search, but I see you've already tried posting to that thread. Maybe we can get @Rajesh16 's attention here, and see if they've made any progress.

 

Also, maybe bumping this thread back to the top will get some more eyes on it.

Leland Scanlan

HubSpot Developer Support
0 Votes
PaulRBerg
Contributeur

Embed Chat Into A ReactJS SPA

Résolue

Any updates on this?

I tried these methods:

1. Add the script in "index.html"
2. Append the script to the "body" element in a "useEffect" Hook defined in the top-level "App" component

None worked (with React 16.11.0). Unfortunately, this is a showstopper, forcing me to go find another live chat product.

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script src="https://js.hs-scripts.com/MY_ID.js" async></script>
</body>
React.useEffect(() => {
  (function loadHubSpot() {
    const d = document;
    const s = d.createElement("script");

    s.src="https://js.hs-scripts.com/MY_ID.js";
    s.async = true;
    d.getElementsByTagName("body")[0].appendChild(s);
  })();
}, [])

 

0 Votes
anitaernszt
Membre

Embed Chat Into A ReactJS SPA

Résolue

Try adding <script .... /> to the <head></head> component instead of <body></body>

I've added to index.html this way and it's working for me

slowtokyo
Participant

Embed Chat Into A ReactJS SPA

Résolue

This worked for me as well.

padecaluwe
Membre

Embed Chat Into A ReactJS SPA

Résolue

we have the same problem here, nothing worked

0 Votes
alexlisong
Membre

Embed Chat Into A ReactJS SPA

Résolue

Have same issue here, any updates?

0 Votes