What's New in v5
Version 5 is a from scratch rewrite. A lot of effort went into this release. If you find this package useful and have the means, please consider a small donation 🙂
The entire project has been rewritten in typescript for a more robust typescript experience. New issue and pull request templates have been added along with a contributing guide for anyone who would like to contribute to the project. Lastly this documentation site has been created to serve as a knowledge base for all things related to the IdleTimer project. If you have any ideas for sections, demos or tutorials to add feel free to start a discussion or open an issue.
If you are upgrading from v4 there are a few breaking changes that you should take into account. The rest of the API has not changed. Only new features and improvements to existing ones.
Breaking Changes#
Removal of <IdleTimer />
Component#
The major change in v5 is the removal of the IdleTimer
component. It has been
replaced with the withIdleTimer higher order component.
You can use the higher order component to create the IdleTimer
component if you
wish and it would be a drop in replacement for the v4 component. A guide on how
to do that can be found here.
Simplified Cross Tab API#
The Cross Tab feature has been rewritten from the ground up. It is now much smaller and more specialized for this particular use case. All the edge cases have been solved and the API surface has been reduced to the essentials.
In v4, the crossTab
prop used to take a boolean
or an object
of configuration
options. Now in v5, the crossTab
prop only takes a boolean
. This is how you
enable or disable the Cross Tab Reconciliation feature. It is disabled by default.
All of the rest of the options from v4 have been set to sane defaults.
This results in a more simple API surface and less type ambiguity.
Events are emitted on all tabs by default and leader election has been reduced
down to the leaderElection
prop and the isLeader
method. See more about leader
election in the next section.
A new property has been added called syncTimers
. It takes a number
that
represents the throttle value in milliseconds for the sync function. Every user
action that is defined in the events
array will be replicated to all tabs, thus
keeping the timeouts across tabs roughly in sync. By default this property has
a value of 0 which is equivalent to disabled. Set it to the maximum amount of
drift between timeouts that is tolerable for you project. Setting it too low will
cause a lot of messaging between tabs. A good place to start is around 200
.
In v4 the following code...
// Hookconst idleTimer = useIdleTimer({crossTab: {type: undefined,channelName: 'idle-timer',fallbackInterval: 2000,responseTime: 100,removeTimeout: 1000 * 60,emitOnAllTabs: true}})// Component<IdleTimer crossTab={{type: undefined,channelName: 'idle-timer',fallbackInterval: 2000,responseTime: 100,removeTimeout: 1000 * 60,emitOnAllTabs: false}} />
Becomes this in v5...
// Hookconst idleTimer = useIdleTimer({crossTab: true})// Higher Order Component Wrapped<IdleTimer crossTab />
With the new syncTimers
feature:
// Hookconst idleTimer = useIdleTimer({crossTab: true,syncTimers: 200})// Higher Order Component Wrapped<IdleTimer crossTab syncTimers={200} />
If you are using multiple IdleTimer instances on the same page, you can use the
name
prop to isolate crossTab events to their respective instances.
// Hookconst logoutTimer = useIdleTimer({timeout: 1000 * 60 * 30,crossTab: true,syncTimers: 200,name: 'logout-timer'})const activityTimer = useIdleTimer({timeout: 1000 * 60 * 5,crossTab: true,syncTimers: 200,name: 'activity-timer'})// Higher Order Component Wrapped<IdleTimer crossTab timeout={1000 * 60 * 30} syncTimers={200} name='logout-timer' /><IdleTimer crossTab timeout={1000 * 60 * 5} syncTimers={200} name='activity-timer' />
There is also a new method getTabId() that will
return the current tabs id that is used internally by IdleTimer's crossTab
feature.
Leader Election#
In v4 Leader Election was enabled by default and would automatically emit events on only the leader tab. In v5, leader election has to be enabled manually by setting the leaderElection property. In order to emit events only on the leader, you can use the isLeader method inside your event handlers.
import { useIdleTimer } from 'react-idle-timer'const App = () => {const { isLeader } = useIdleTimer({crossTab: true,leaderElection: true,onIdle: () => {if (isLeader()) {// I am the leader, perform remote action} else {// I am not the leader, perform local action}}})return <></>}
Last Active and Idle Times#
The getLastActiveTime()
and getLastIdleTime()
methods now return
Date
objects instead of a timestamp. This will eliminate the need to cast to a Date
before formatting. If you want to get the old timestamp you can just call
getLastActiveTime().getTime()
and getLastIdleTime().getTime()
.
Non Breaking Changes#
Removal of passive
and capture
Props#
The passive
and capture
props have been removed and are automatically set to
true. There is no good reason a user would want to set either of them to false
and as such, they have been removed to reduce complexity and API surface. If you
were to leave these props set in your useIdleTimer
hook or withIdleTimer
higher order component, they wouldn't do anything. This is not necessarily a
breaking change but something that is good to be aware of while migrating.
New Features#
Web Worker Timers#
When a window is backgrounded, the browser will throttle timers in an attempt to
save on CPU cycles. This can cause IdleTimer to produce delayed and inaccurate
results. To combat this, the timers interface has been exposed as a property and
an implementation of WebWorker thread timers are exposed in order to keep them
alive when a window is throttled by the browser. The worker is provided via a
blob URL and this can cause strict CSP rules to be violated. It is strongly
suggested that you update your rule to allow the worker to run so the timers are
accurate. If this is not an option, you can omit the timers
property and use
the default main thread timers. Main thread timers are the default in order to
allow projects that wish to omit worker thread timers to tree shake out the
worker code. If you decide to enable worker timers, take a look at
Testing Considerations for information on how
to mock these timers for your test suites.
import { useIdleTimer, workerTimers } from 'react-idle-timer'export const App = () => {const idleTimer = useIdleTimer({ timers: workerTimers })return (<h1>Using web worker timers!</h1>)}
Integrated Prompting#
A common use case for IdleTimer is to detect when your user is idle and then open a modal to ask the user if they are still there. There have been a lot of questions about how to do this, so in v5, the IdleTimer will handle it for you.
The new promptTimeout
configuration option along with the onPrompt
callback
and isPrompted()
state getter is all you need to prompt your user before going
idle.
Here is a minimal example. More complete examples can be found in the features section.
export function App () {// Set timeout valuesconst timeout = 1000 * 60 * 30const promptTimeout = 1000 * 30// Modal open stateconst [open, setOpen] = useState(false)// Time before idleconst [remaining, setRemaining] = useState(0)const onPrompt = () => {// onPrompt will be called after the timeout value is reached// In this case 30 minutes. Here you can open your prompt.// All events are disabled while the prompt is active.// If the user wishes to stay active, call the `reset()` method.// You can get the remaining prompt time with the `getRemainingTime()` method,setOpen(true)setRemaining(promptTimeout)}const onIdle = () => {// onIdle will be called after the promptTimeout is reached.// In this case 30 seconds. Here you can close your prompt and// perform what ever idle action you want such as log out your user.// Events will be rebound as long as `stopOnMount` is not set.setOpen(false)setRemaining(0)}const onActive = () => {// onActive will only be called if `reset()` is called while `isPrompted()`// is true. Here you will also want to close your modal and perform// any active actions.setOpen(false)setRemaining(0)}const { getRemainingTime, isPrompted, activate } = useIdleTimer({timeout,promptTimeout,onPrompt,onIdle,onActive})const handleStillHere = () => {setOpen(false)activate()}useEffect(() => {const interval = setInterval(() => {if (isPrompted()) {setRemaining(Math.ceil(getRemainingTime() / 1000))}}, 1000)return () => {clearInterval(interval)}}, [getRemainingTime, isPrompted])return (<div className='modal' style={{ display: open ? 'block' : 'none' }}><p>Logging you out in {remaining} seconds</p><button onClick={handleStillHere}>Im Still Here</button></div>)}
Cross Tab Messaging#
IdleTimer now exposes its cross tab messaging layer so you can easily broadcast arbitrary messages to all instances of your page running in all tabs. This is exposed by two new APIs...
a message callback:
onMessage?: (data: string | number | object) => void
and a message emitter:
message (data: string | number | object, emitOnSelf?: boolean): void
Using these mechanisms, you can use IdleTimer to send messages to all instances of your application. Here is a basic example with redux.
import { useIdleTimer } from 'react-idle-timer'import { useDispatch } from 'react-redux'import { logAction } from './Actions'export function App () {// Action dispatcher (redux)const dispatch = useDispatch()// Message handlerconst onMessage = data => {switch (data.action) {case 'LOGOUT_USER':dispatch(logoutAction())break// More actionsdefault:// no op}}// IdleTimer instanceconst { message } = useIdleTimer({ onMessage })// Logout button clickconst onLogoutClick = () => {// Tell all tabs to log the user out.// Passing true as a second parameter will// also emit the event in this tab.message({ action: 'LOGOUT_USER' }, true)}return (<button onClick={onLogoutClick}>Logout</button>)}
More complete examples can be found in the features section.
Immediate Events#
A new configuration option called immediateEvents
has been added.
immediateEvents?: IEventsType[]
Any events added to immediateEvents
will immediately switch the user into an
idle or prompted state, calling onIdle
or onPrompt
respectively. This is
useful for events such as pagehide
, where you want to bypass the timeout and
go directly to an idle state.
If an event is set in both events
and immediateEvents
, the immediate event
will take precedence allowing you to override the default events if you like.
Higher Order Component#
With the removal of the IdleTimer
component, the withIdleTimer
higher order
component is now the default way to add IdleTimer to class components. More about
the higher order component here.
Provider#
If you need to access the IdleTimer API in nested children, you can opt to use
the IdleTimerProvider
rather than drilling props through your App's hierarchy.
More about the Provider and related exports here.
Activate Method#
The reset
method will now reset IdleTimer to it's initial state only. It will no
longer call onActive
if the user was idle. This functionality has been replaced
by the activate() method. You can call activate()
to
manually activate a user, resetting the timer and calling onActive
if the user
was idle.