diff --git a/src/constants.js b/src/constants.js
new file mode 100644
index 0000000..710d443
--- /dev/null
+++ b/src/constants.js
@@ -0,0 +1,13 @@
+export const LEVEL_DEBUG = 'debug';
+export const LEVEL_INFO = 'info';
+export const LEVEL_WARN = 'warn';
+export const LEVEL_ERROR = 'error';
+export const LEVEL_CRITICAL = 'critical';
+
+export default {
+ [LEVEL_DEBUG]: 1,
+ [LEVEL_INFO]: 2,
+ [LEVEL_WARN]: 3,
+ [LEVEL_ERROR]: 4,
+ [LEVEL_CRITICAL]: 5,
+}
diff --git a/src/error-boundary.js b/src/error-boundary.js
new file mode 100644
index 0000000..4a8da34
--- /dev/null
+++ b/src/error-boundary.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import invariant from 'tiny-invariant';
+import VALID_LEVELS, { LEVEL_ERROR } from './constants';
+import { Context, getRollbarFromContext } from './provider';
+
+const INITIAL_ERROR_STATE = { hasError: false, error: null };
+
+export class ErrorBoundary extends Component {
+ static contextType = Context;
+
+ static propTypes = {
+ fallbackUI: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
+ errorMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ extra: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
+ level: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ callback: PropTypes.func,
+ }
+
+ static defaultProps = {
+ level: LEVEL_ERROR,
+ }
+
+ constructor(props) {
+ super(props);
+ invariant(VALID_LEVELS.includes(props.level), `${props.level} is not a valid level setting for Rollbar`);
+ this.state = { ...INITIAL_ERROR_STATE };
+ }
+
+ static getDerivedStateFromError(error) {
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error, info) {
+ const { errorMessage, extra, level: targetLevel, callback } = this.props;
+ const custom = utils.value(extra, {}, error, info);
+ const data = { ...info, ...custom };
+ const level = utils.value(targetLevel, LEVEL_ERROR, error, info);
+ const rollbar = getRollbarFromContext(this.context);
+ if (!errorMessage) {
+ rollbar[level](error, data, callback);
+ } else {
+ let logMessage = utils.value(errorMessage, '', error, info);
+ rollbar[level](logMessage, error, data, callback);
+ }
+ }
+
+ resetError = () => {
+ this.setState(INITIAL_ERROR_STATE);
+ }
+
+ render() {
+ const { hasError, error } = this.state;
+ const { fallbackUI, children } = this.props;
+
+ if (!hasError) {
+ return children;
+ }
+
+ if (!fallbackUI) {
+ return null;
+ }
+
+ if (React.isValidElement(fallbackUI)) {
+ return ;
+ }
+
+ if (typeof fallbackUI === 'function') {
+ const fallbackComponent = fallbackUI(error, this.resetError);
+ return React.isValidElement(fallbackComponent) ? fallbackComponent : null;
+ }
+
+ return null;
+ }
+}
diff --git a/src/history-context.js b/src/history-context.js
new file mode 100644
index 0000000..c791f2c
--- /dev/null
+++ b/src/history-context.js
@@ -0,0 +1,24 @@
+import Rollbar from 'rollbar';
+import invariant from 'tiny-invariant';
+
+export function historyContext(rollbar, { formatter, filter }) {
+ invariant(rollbar instanceof Rollbar, 'historyContext must have an instance of Rollbar');
+ invariant(formatter == null || typeof formatter === 'function', `formatter option must be a function, received ${typeof formatter} instead`);
+ invariant(filter == null || typeof filter === 'function', `filter option must be a function, received ${typeof filter} instead`);
+ // v4 of history.listen callback signature is (location, action)
+ // v5 of history.listen callback signature is ({ location, action })
+ // this implementation translates it to work for both
+ return (v4location, v4action) => {
+ let { action, location } = v4location;
+ if (v4action) {
+ action = v4action;
+ location = v4location;
+ }
+ if (filter && !filter(location, action)) {
+ return;
+ }
+ const context = formatter ? formatter(location, action) : location.pathname;
+ invariant(typeof context === 'string', 'formatter must return a string value to set the context');
+ rollbar.configure({ payload: { context }});
+ }
+}
diff --git a/src/hooks/index.js b/src/hooks/index.js
new file mode 100644
index 0000000..26a27de
--- /dev/null
+++ b/src/hooks/index.js
@@ -0,0 +1,5 @@
+export { useRollbar } from './use-rollbar';
+export { useRollbarConfiguration } from './use-rollbar-config';
+export { useRollbarContext } from './use-rollbar-context';
+export { useRollbarPerson } from './use-rollbar-person';
+export { useRollbarCaptureEvent } from './use-rollbar-capture-event';
diff --git a/src/hooks/use-rollbar-capture-event.js b/src/hooks/use-rollbar-capture-event.js
new file mode 100644
index 0000000..6168b83
--- /dev/null
+++ b/src/hooks/use-rollbar-capture-event.js
@@ -0,0 +1,12 @@
+import invariant from 'tiny-invariant';
+import VALID_LEVELS, { LEVEL_DEBUG, LEVEL_INFO, LEVEL_CRITICAL } = '../constants';
+import [ useRollbar } from './use-rollbar';
+
+export function useRollbarCaptureEvent(metadata, level = LEVEL_INFO) {
+ invariant(VALID_LEVELS[level] <= LEVEL_CRITICAL && VALID_LEVELS[level] >= LEVEL_DEBUG, '')
+ const rollbar = useRollbar();
+ useEffect(() => {
+ rollbar.captureEvent(metadata, level);
+ }, [metadata]);
+}
+
diff --git a/src/hooks/use-rollbar-config.js b/src/hooks/use-rollbar-config.js
new file mode 100644
index 0000000..ccffc11
--- /dev/null
+++ b/src/hooks/use-rollbar-config.js
@@ -0,0 +1,6 @@
+import [ useRollbar } from './use-rollbar';
+
+export function useRollbarConfiguration(config) {
+ const rollbar = useRollbar();
+ rollbar.configure(config);
+}
diff --git a/src/hooks/use-rollbar-context.js b/src/hooks/use-rollbar-context.js
new file mode 100644
index 0000000..45856cc
--- /dev/null
+++ b/src/hooks/use-rollbar-context.js
@@ -0,0 +1,21 @@
+import invariant from 'tiny-invariant';
+import { useRollbar } from './use-rollbar';
+import { useRollbarConfiguration } from './use-rollbar-config';
+
+// Simple version does its job
+// export function useRollbarContext(context) {
+// useRollbarConfiguration({ payload: { context }});
+// }
+
+// Complex version will set the context when part of the tree and reset back to original context when removed
+export function useRollbarContext(ctx = '', isLayout = false) {
+ invariant(typeof ctx === 'string', '`ctx` must be a string');
+ const rollbar = useRollbar();
+ (isLayout ? useLayoutEffect : useEffect)(() => {
+ const origCtx = rollbar.options.payload.context;
+ rollbar.configure({ payload: { context: ctx }});
+ return () => {
+ rollbar.configure({ payload: { context: origCtx }});
+ };
+ }, [ctx]);
+}
diff --git a/src/hooks/use-rollbar-logs.js b/src/hooks/use-rollbar-logs.js
new file mode 100644
index 0000000..aa08172
--- /dev/null
+++ b/src/hooks/use-rollbar-logs.js
@@ -0,0 +1,42 @@
+// EXPERIMENTAL
+// NOT EXPORTED AS PART OF PUBLIC API YET
+// NO TEST COVERAGE
+
+import { useEffect, useLayoutEffect } from 'react';
+import invariant from 'tiny-invariant';
+import VALID_LEVELS, { LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR, LEVEL_CRITICAL } from '../constants';
+import { useRollbar } from './use-rollbar';
+
+const LOG = 'log';
+
+function useRollbarNotify(type, isLayout, ...args) {
+ invariant(type === LOG || VALID_LEVELS[type] >= LEVEL_DEBUG && VALID_LEVELS[type] <= LEVEL_CRITICAL, `cannot notify rollbar using method '${type}'`);
+ const rollbar = useRollbar();
+ (isLayout ? useLayoutEffect : useEffect)(() => {
+ rollbar[type](...args);
+ }, args)
+}
+
+export function useRollbarLog(isLayout, ...args) {
+ useRollbarNotify(LOG, isLayout, ...args);
+}
+
+export function useRollbarDebug(isLayout, ...args) {
+ useRollbarNotify(LEVEL_DEBUG, isLayout, ...args);
+}
+
+export function useRollbarInfo(isLayout, ...args) {
+ useRollbarNotify(LEVEL_INFO, isLayout, ...args);
+}
+
+export function useRollbarWarn(isLayout, ...args) {
+ useRollbarNotify(LEVEL_WARN, isLayout, ...args);
+}
+
+export function useRollbarError(isLayout, ...args) {
+ useRollbarNotify(LEVEL_ERROR, isLayout, ...args);
+}
+
+export function useRollbarCritical(isLayout, ...args) {
+ useRollbarNotify(LEVEL_ERROR, isLayout, ...args);
+}
diff --git a/src/hooks/use-rollbar-person.js b/src/hooks/use-rollbar-person.js
new file mode 100644
index 0000000..f6bd24b
--- /dev/null
+++ b/src/hooks/use-rollbar-person.js
@@ -0,0 +1,5 @@
+import { useRollbarConfiguration } from './use-rollbar-config';
+
+export function useRollbarPerson(person) {
+ useRollbarConfiguration({ payload: { person }});
+}
diff --git a/src/hooks/use-rollbar.js b/src/hooks/use-rollbar.js
new file mode 100644
index 0000000..666b52f
--- /dev/null
+++ b/src/hooks/use-rollbar.js
@@ -0,0 +1,7 @@
+import { useContext } from 'react';
+import { Context, getRollbarFromContext } from '../provider';
+
+export function useRollbar() {
+ const context = useContext(Context);
+ return getRollbarFromContext(context);
+}
diff --git a/src/hooks/use-scoped-rollbar-config.js b/src/hooks/use-scoped-rollbar-config.js
new file mode 100644
index 0000000..df74050
--- /dev/null
+++ b/src/hooks/use-scoped-rollbar-config.js
@@ -0,0 +1,15 @@
+// EXPERIMENTAL
+// NOT EXPORTED AS PART OF PUBLIC API YET
+// NO TEST COVERAGE
+
+import { useContext } from 'react';
+import { Context, getRollbarFromContext, getRollbarConstructorFromContext } from '../provider';
+
+export function useScopedConfiguration(config) {
+ const ctx = useContext(Context);
+ const rollbar = getRollbarFromContext(ctx);
+ const ctor = getRollbarConstructorFromContext(ctx);
+ const rollbar = new ctor(base.options);
+ rollbar.configure(config);
+ return rollbar;
+}
diff --git a/src/hooks/utils.js b/src/hooks/utils.js
new file mode 100644
index 0000000..56ce4ac
--- /dev/null
+++ b/src/hooks/utils.js
@@ -0,0 +1,15 @@
+import { useRef } from 'react';
+
+// EXPERIMENTAL (NOT IN USE): wrap the instance of rollbar to prevent modification
+export function wrapRollbarApi(rollbar) {
+ const rb = useRef(rollbar);
+ return {
+ log: (...args) => rb.current.log(...args),
+ debug: (...args) => rb.current.debug(...args),
+ info: (...args) => rb.current.info(...args),
+ warn: (...args) => rb.current.warn(...args),
+ error: (...args) => rb.current.error(...args),
+ critical: (...args) => rb.current.critical(...args),
+ captureEvent: (...args) => rb.current.captureEvent(...args),
+ }
+}
diff --git a/src/index.js b/src/index.js
index 15f3bc6..5cab95d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,347 +1,7 @@
-import React, { Component, createContext, useContext, useEffect, useLayoutEffect, useRef } from 'react';
-import PropTypes from 'prop-types';
-import Rollbar from 'rollbar';
-import invariant from 'tiny-invariant';
-
-const LEVEL_DEBUG = 'debug';
-const LEVEL_INFO = 'info';
-const LEVEL_WARN = 'warn';
-const LEVEL_ERROR = 'error';
-const LEVEL_CRITICAL = 'critical';
-
-const Context = createContext();
-Context.displayName = 'Rollbar';
-
-const RollbarInstance = Symbol('RollbarInstance');
-const BaseOptions = Symbol('BaseOptions');
-const RollbarCtor = Symbol('RollbarCtor');
-
-function value(val, defaultTo, ...args) {
- if (typeof val === 'function') {
- return val(...args);
- }
- return val;
-}
-
-function wrapValue(val, defaultAs) {
- return (defaultTo, ...args) => value(val, defaultAs === undefined ? defaultTo : defaultAs, ...args);
-}
-
-export function historyContext(rollbar, { formatter, filter }) {
- invariant(rollbar instanceof Rollbar, 'historyContext must have an instance of Rollbar');
- invariant(formatter == null || typeof formatter === 'function', `formatter option must be a function, received ${typeof formatter} instead`);
- invariant(filter == null || typeof filter === 'function', `filter option must be a function, received ${typeof filter} instead`);
- return (v4location, v4action) => {
- let { action, location } = v4location;
- if (v4action) {
- action = v4action;
- location = v4location;
- }
- if (filter && !filter(location, action)) {
- return;
- }
- const context = formatter ? formatter(location, action) : location.pathname;
- rollbar.configure({ payload: { context }});
- }
-}
-
-export class Provider extends Component {
- static propTypes = {
- Rollbar: PropTypes.func,
- config: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
- instance: PropTypes.instanceOf(Rollbar),
- }
-
- constructor(props) {
- super(props);
- const { config, Rollbar: ctor = Rollbar, instance } = this.props;
- invariant(!instance || instance instanceof Rollbar, 'providing `instance` must be of type Rollbar');
- const options = typeof config === 'function' ? config() : config;
- const rollbar = instance || new ctor(options);
- // TODO: use isUncaught to filter if this is 2nd Provider added
- // unless customer wants that
- this.state = { rollbar, options };
- }
-
- // componentDidUpdate()
-
- render() {
- const { children, Rollbar: ctor = Rollbar } = this.props;
- const { rollbar, options } = this.state;
-
- return (
-
- {children}
-
- )
- }
-}
-
-export class RollbarContext extends Component {
- static propTypes = {
- context: PropTypes.string.isRequired,
- onRender: PropTypes.bool,
- }
-
- static defaultProps = {
- onRender: false,
- }
-
- static contextType = Context;
-
- firstRender = true;
-
- constructor(props) {
- super(props);
- this.state = { previousContext: null };
- }
-
- // static getDerivedStateFromProps() {}
-
- changeContext = (storePrevious = true) => {
- const { [RollbarInstance]: rollbar } = this.context;
- const { context } = this.props;
- if (storePrevious) {
- this.setState({ previousContext: rollbar.options.payload.context });
- }
- rollbar.configure({ payload: { context }});
- }
-
- componentDidMount() {
- const { onRender } = this.props;
- if (!onRender) {
- this.changeContext(true);
- }
- }
-
- componentDidUpdate() {
- const { onRender } = this.props;
- if (!onRender) {
- this.changeContext(false);
- }
- }
-
- componentWillUnmount() {
- const { [RollbarInstance]: rollbar } = this.context;
- const { previousContext } = this.state;
- rollbar.configure({ payload: { context: previousContext }});
- }
-
- render() {
- const { onRender } = this.props;
- if (onRender && this.firstRender) {
- this.changeContext(true);
- }
- this.firstRender = false;
- return this.props.children;
- }
-}
-
-// export class RollbarConfiguration extends Component {
-// static propTypes = {
-// options: PropTypes.object.isRequired,
-// }
-
-// static contextType = Context;
-
-// constructor(props) {
-// super(props);
-// this.state = { parentConfig: null };
-// }
-
-// componentDidMount() {
-// const { [RollbarInstance]: rollbar } = this.context;
-// const { options } = this.props;
-// // need to clone this somehow to prevent downstream changes from manipulating it
-// const parentConfig = (o => o)(rollbar.options);
-// this.setState({ parentConfig });
-// rollbar.configure(options);
-// }
-
-// componentWillUnmount() {
-// const { [RollbarInstance]: rollbar } = this.context;
-// const { parentConfig } = this.state;
-// rollbar.configure(parentConfig);
-// }
-
-// render() {
-// return this.props.children;
-// }
-// }
-
-const NOOP = () => {};
-const INITIAL_ERROR_STATE = { hasError: false, error: null };
-
-export class ErrorBoundary extends Component {
- static contextType = Context;
-
- static propTypes = {
- fallbackUI: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
- errorMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
- extra: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
- level: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
- callback: PropTypes.func,
- }
-
- static defaultProps = {
- level: 'error',
- }
-
- constructor(props) {
- super(props);
- this.state = { ...INITIAL_ERROR_STATE };
- }
-
- static getDerivedStateFromError(error) {
- return { hasError: true, error };
- }
-
- componentDidCatch(error, info) {
- const { errorMessage, extra, level: targetLevel, callback } = this.props;
- const custom = value(extra, {}, error, info);
- const data = { ...info, ...custom };
- const level = value(targetLevel, 'error', error, info);
- const { [RollbarInstance]: rollbar } = this.context;
- if (!errorMessage) {
- rollbar[level](error, data, callback);
- } else {
- let logMessage = value(errorMessage, '', error, info);
- rollbar[level](logMessage, error, data, callback);
- }
- }
-
- resetError = () => {
- this.setState(INITIAL_ERROR_STATE);
- }
-
- // wrapRollbar = (rollbar, children) => {
- // return (
- //
- // {children}
- //
- // );
- // }
-
- // getChildren = () => {
- render() {
- const { hasError, error } = this.state;
- const { fallbackUI, children } = this.props;
-
- if (!hasError) {
- return children;
- }
-
- if (!fallbackUI) {
- return null;
- }
-
- if (React.isValidElement(fallbackUI)) {
- return ;
- }
-
- if (typeof fallbackUI === 'function') {
- const fallbackComponent = fallbackUI(error, this.resetError);
- return React.isValidElement(fallbackComponent) ? fallbackComponent : null;
- }
-
- return null;
- }
-
- // render() {
- // const { rollbar, doWrapRollbar } = this.state;
- // const children = this.getChildren();
- // if (doWrapRollbar) {
- // return this.wrapRollbar(rollbar, children);
- // }
- // return children;
- // }
-}
-
-export function useRollbar() {
- const { [RollbarInstance]: rollbar } = useContext(Context);
- return rollbar;
-}
-
-export function useRollbarConfiguration(config) {
- const rollbar = useRollbar();
- rollbar.configure(config);
-}
-
-export function useScopedConfiguration(config) {
- const { [RollbarInstance]: base, [RollbarCtor]: ctor } = useContext(Context);
- // const base = useRollbar();
- const rollbar = new ctor(base.options);
- rollbar.configure(config);
- return rollbar;
-}
-
-export function useRollbarPerson(person) {
- useRollbarConfiguration({ payload: { person }});
-}
-
-export function useRollbarContext(context) {
- useRollbarConfiguration({ payload: { context }});
-}
-
-function useRollbarNotify(type, isLayout, ...args) {
- const rollbar = useRollbar();
- (isLayout ? useLayoutEffect : useEffect)(() => {
- rollbar[type](...args);
- }, args)
-}
-
-function wrapRollbarApi(rollbar) {
- const rb = useRef(rollbar);
- return {
- log: (...args) => rb.current.log(...args),
- debug: (...args) => rb.current.debug(...args),
- info: (...args) => rb.current.info(...args),
- warn: (...args) => rb.current.warn(...args),
- error: (...args) => rb.current.error(...args),
- critical: (...args) => rb.current.critical(...args),
- captureEvent: (...args) => rb.current.captureEvent(...args),
- }
-}
-
-export function useRollbarLog(...args) {
- useRollbarNotify('log', ...args);
-}
-
-export function useRollbarDebug(...args) {
- useRollbarNotify('debug', ...args);
-}
-
-export function useRollbarInfo(...args) {
- useRollbarNotify('info', ...args);
-}
-
-export function useRollbarWarn(...args) {
- useRollbarNotify('warn', ...args);
-}
-
-export function useRollbarError(...args) {
- useRollbarNotify('error', ...args);
-}
-
-export function useRollbarCritical(...args) {
- useRollbarNotify('critical', ...args);
-}
-
-export function useRollbarCaptureEvent(metadata, level) {
- const rollbar = useRollbar();
- useEffect(() => {
- rollbar.captureEvent(metadata, level);
- }, [metadata]);
-}
-
-export function useContext(ctx = '', isLayout = false) {
- invariant(ctx && typeof ctx === 'string', '`ctx` must be a non-empty string');
- const rollbar = useRollbar();
- (isLayout ? useLayoutEffect : useEffect)(() => {
- const origCtx = rollbar.options.payload.context;
- rollbar.configure({ payload: { context: ctx }});
- return () => {
- rollbar.configure({ payload: { context: origCtx }});
- };
- }, [ctx]);
-}
+import VALID_LEVELS, { LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR, LEVEL_CRITICAL } from './constants';
+export { VALID_LEVELS, LEVEL_DEBUG, LEVEL_INFO, LEVEL_WARN, LEVEL_ERROR, LEVEL_CRITICAL }
+export { historyContext } from './history-context';
+export { Provider } from './provider';
+export { ErrorBoundary } from './error-boundary';
+export { RollbarContext } from './rollbar-context';
+export * from './hooks';
diff --git a/src/provider.js b/src/provider.js
new file mode 100644
index 0000000..d4db946
--- /dev/null
+++ b/src/provider.js
@@ -0,0 +1,57 @@
+import React, { Component, createContext, useContext, useEffect, useLayoutEffect, useRef } from 'react';
+import PropTypes from 'prop-types';
+import Rollbar from 'rollbar';
+import invariant from 'tiny-invariant';
+import * as constants from './constants';
+import * as utils from './utils';
+
+export const Context = createContext();
+Context.displayName = 'Rollbar';
+
+const RollbarInstance = Symbol('RollbarInstance');
+const BaseOptions = Symbol('BaseOptions');
+const RollbarCtor = Symbol('RollbarCtor');
+
+export function getRollbarFromContext(context) {
+ const { [RollbarInstance]: rollbar } = context;
+ return rollbar;
+}
+
+export function getRollbarConstructorFromContext(context) {
+ const { [RollbarCtor]: ctor } = context;
+ return ctor;
+}
+
+export class Provider extends Component {
+ static propTypes = {
+ Rollbar: PropTypes.func,
+ config: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
+ instance: PropTypes.instanceOf(Rollbar),
+ }
+
+ constructor(props) {
+ super(props);
+ const { config, Rollbar: ctor = Rollbar, instance } = this.props;
+ invariant(!instance || instance instanceof Rollbar, 'providing `instance` must be of type Rollbar');
+ const options = typeof config === 'function' ? config() : config;
+ const rollbar = instance || new ctor(options);
+ // TODO: use isUncaught to filter if this is 2nd Provider added
+ // unless customer wants that
+ this.state = { rollbar, options };
+ }
+
+
+
+ // componentDidUpdate()
+
+ render() {
+ const { children, Rollbar: ctor = Rollbar } = this.props;
+ const { rollbar, options } = this.state;
+
+ return (
+
+ {children}
+
+ )
+ }
+}
diff --git a/src/rollbar-configuration.js b/src/rollbar-configuration.js
new file mode 100644
index 0000000..7d7d423
--- /dev/null
+++ b/src/rollbar-configuration.js
@@ -0,0 +1,40 @@
+// EXPERIMENTAL
+// NOT EXPORTED AS PART OF PUBLIC API YET: no current test coverage
+
+// PURPOSE provide a wrapping around Rollbar configuration for a subtree
+
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import { Context, getRollbarFromContext } from './provider';
+
+export class RollbarConfiguration extends Component {
+ static propTypes = {
+ options: PropTypes.object.isRequired,
+ }
+
+ static contextType = Context;
+
+ constructor(props) {
+ super(props);
+ this.state = { parentConfig: null };
+ }
+
+ componentDidMount() {
+ const rollbar = getRollbarFromContext(this.context);
+ const { options } = this.props;
+ // TODO: need to clone this somehow to prevent downstream changes from manipulating it
+ const parentConfig = (o => o)(rollbar.options);
+ this.setState({ parentConfig });
+ rollbar.configure(options);
+ }
+
+ componentWillUnmount() {
+ const rollbar = getRollbarFromContext(this.context);
+ const { parentConfig } = this.state;
+ rollbar.configure(parentConfig);
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
diff --git a/src/rollbar-context.js b/src/rollbar-context.js
new file mode 100644
index 0000000..2345007
--- /dev/null
+++ b/src/rollbar-context.js
@@ -0,0 +1,65 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+// import Rollbar from 'rollbar';
+import invariant from 'tiny-invariant';
+import { Context, getRollbarFromContext } from './provider';
+
+export class RollbarContext extends Component {
+ static propTypes = {
+ context: PropTypes.string.isRequired,
+ onRender: PropTypes.bool,
+ }
+
+ static defaultProps = {
+ onRender: false,
+ }
+
+ static contextType = Context;
+
+ firstRender = true;
+
+ constructor(props) {
+ super(props);
+ this.state = { previousContext: null };
+ }
+
+ // static getDerivedStateFromProps() {}
+
+ changeContext = (storePrevious = true) => {
+ const rollbar = getRollbarFromContext(this.context);
+ const { context } = this.props;
+ if (storePrevious) {
+ this.setState({ previousContext: rollbar.options.payload.context });
+ }
+ rollbar.configure({ payload: { context }});
+ }
+
+ componentDidMount() {
+ const { onRender } = this.props;
+ if (!onRender) {
+ this.changeContext(true);
+ }
+ }
+
+ componentDidUpdate() {
+ const { onRender } = this.props;
+ if (!onRender) {
+ this.changeContext(false);
+ }
+ }
+
+ componentWillUnmount() {
+ const rollbar = getRollbarFromContext(this.context);
+ const { previousContext } = this.state;
+ rollbar.configure({ payload: { context: previousContext }});
+ }
+
+ render() {
+ const { onRender } = this.props;
+ if (onRender && this.firstRender) {
+ this.changeContext(true);
+ }
+ this.firstRender = false;
+ return this.props.children;
+ }
+}
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..465023a
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,10 @@
+export function value(val, defaultTo, ...args) {
+ if (typeof val === 'function') {
+ return val(...args);
+ }
+ return val;
+}
+
+export function wrapValue(val, defaultAs) {
+ return (defaultTo, ...args) => value(val, defaultAs === undefined ? defaultTo : defaultAs, ...args);
+}