External router integration¶
Contract¶
To communicate between iframe and its parent site is used window.postMessage function. Means betbook sends message to parent site.
where pathname will contain betbook route string. For example: (“/live/tableTennis”, “/events/yurii-volkov-andrii-troian-11691674“)
window.addEventListener("message", data => {
if (data.type === "navigation") {
// TODO: handle data.path and repalace route
}
});
Route refresh¶
To update browser url with new route use History replaceState API in particular framework router.
To avoid conflicts with parent site routing it would be a good idea to add some route prefix to ‘navigation’ data.
Example
Add prefix /sport to all routes received from betbook. Then full routes for replaceAPI will look like: '/sport/live/tableTennis', '/sport/events/yurii-volkov-andrii-troian-11691674'
React useful hook¶
If parent site uses react as main framework, we provide useful useNavigation hook based on react-router-dom
Utils from this hooks can be used in non-react integration also, details in jsdocs comments.
import { useCallback } from "react";
import { useLocation, useNavigate } from "react-router-dom";
const IFRAME_PREFIX = "sports";
/**
* Extracts the language from the given path.
*
* @param {string} path - The URL path.
* @param {Array} languages - Array of available languages.
* @returns {string|undefined} - The language code if found, otherwise undefined.
*/
export const getUrlLang = (path, languages) => {
return path.split("/").find(segment => languages.includes(segment));
};
/**
* Prepares the iframe path by removing language and prefix segments.
*
* @param {string} path - The URL path.
* @param {Array} languages - Array of available language codes.
* @param {string} fallbackLang - The default language code.
* @returns {string} - The prepared iframe path (removes lang and prefix)
*/
const prepareIframePath = (path, languages, fallbackLang) => {
const lang = getUrlLang(path, languages) || fallbackLang;
const regex = new RegExp(
`${IFRAME_PREFIX}/${lang}/?|/${lang}/?`,
'g',
);
return path.replace(regex, "");
};
/**
* Prepares the client path by concatenating prefix, language and iframe path segments.
*
* @param {string} path - The iframe path.
* @param {Array} languages - Array of available language codes.
* @param {string} fallbackLang - The default language code.
* @returns {string} - The prepared client path.
*/
const prepareClientPath = (path, languages, fallbackLang) => {
const lang = getUrlLang(path, languages) || fallbackLang;
const preparedIframePath = prepareIframePath(path, languages, fallbackLang);
return preparedIframePath === "/" ? preparedIframePath : `/${IFRAME_PREFIX}/${lang}${preparedIframePath}`;
};
/**
* Custom hook for client-iframe navigation.
*
* @param {string} lang - The current language code.
* @param {Array} langList - Array of supported language codes.
* @returns {Object} - Object containing navigation functions and paths.
*/
export const useNavigation = (lang, langList) => {
const navigateByPath = useNavigate();
const { pathname } = useLocation();
const iframePath = prepareIframePath(pathname, langList, lang);
const clientPath = prepareClientPath(pathname, langList, lang);
/**
* Navigates by path.
*
* @param {Object} options - Navigation options.
* @param {string} options.path - The path to navigate to.
* @param {string} [options.language=lang] - The language code to use.
*/
const navigate = useCallback(
({ path, language = lang }) => {
navigateByPath(prepareClientPath(path, langList, language), { replace: true });
},
[lang, navigateByPath, langList]
);
return {
navigateByPath,
navigate,
iframePath,
clientPath,
};
};
export default useNavigation;
Note
Client can use hook methods to get paths for parent site and iframe taking sport prefix into an account.
// ...imports etc
const prepareUrl = (lang, path = '') => {
return innerAppHost + `/${lang}` + path;
};
function App() {
const { lang, setLang, langCodes } = useLanguageService(); // any language service
const [url, setUrl] = useState(null);
const { navigate, navigateByPath, iframePath, clientPath } = useNavigation(
lang,
langCodes,
);
const routerRef = useRef({ prevPath: iframePath });
useEffect(() => {
const { prevPath } = routerRef.current;
setUrl(prepareUrl(lang, prevPath));
navigate({ path: prevPath, language: lang });
}, [lang, navigate]);
useEffect(() => {
window.addEventListener('message', (data) => {
if (data.type === 'navigation') {
if (path === routerRef.current.prevPath) return;
navigate({ path });
routerRef.current.prevPath = path;
}
});
}, [navigate]);
return (
<iframe
id="betbook"
src={url}
></iframe>
);
}
export default App;