index.tsx 1.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import {useEffect, useState} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import type RRWebPlayer from 'rrweb-player';
  4. import BaseRRWebReplayer from './baseRRWebReplayer';
  5. type RRWebEvents = ConstructorParameters<typeof RRWebPlayer>[0]['props']['events'];
  6. interface Props {
  7. urls: string[];
  8. className?: string;
  9. }
  10. /**
  11. * Downloads a list of replay JSONs, merges the resulting events within the
  12. * JSON and passes it to the replayer.
  13. */
  14. function RRWebReplayer({urls}: Props) {
  15. const [events, setEvents] = useState<RRWebEvents>();
  16. const loadEvents = async () => {
  17. try {
  18. // rrweb's recordings consist of:
  19. // 1) "checkout" phase that essentially records the entire DOM
  20. // 2) incremental updates (DOM changes/events)
  21. //
  22. // The "checkout" phase can be configured to happen at a time interval or
  23. // an event interval. We want to support SDK clients that record a
  24. // single, large JSON, but also clients that record the checkout and
  25. // incremental updates in separate JSON files.
  26. //
  27. // Below we download all of the JSON files and merge them into a large
  28. // list of events for the replayer. The replayer supports having a
  29. // list that has multiple "checkouts".
  30. const data: RRWebEvents[] = await Promise.all(
  31. urls.map(async url => {
  32. const resp = await fetch(url);
  33. const json = await resp.json();
  34. return json.events;
  35. })
  36. );
  37. setEvents(data.flat());
  38. } catch (err) {
  39. Sentry.captureException(err);
  40. }
  41. };
  42. useEffect(() => void loadEvents(), [urls]);
  43. return <BaseRRWebReplayer events={events} />;
  44. }
  45. export default RRWebReplayer;