Skip to content

Commit

Permalink
abstract out useTimeout into hook
Browse files Browse the repository at this point in the history
  • Loading branch information
b-cooper committed Dec 16, 2019
1 parent f371976 commit 6fbb842
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 4 deletions.
19 changes: 16 additions & 3 deletions app/src/components/PrepareModules/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// @flow
import * as React from 'react'
import { useDispatch } from 'react-redux'
import some from 'lodash/some'
import { PrimaryButton, AlertModal, Icon } from '@opentrons/components'
import {
useTimeout,
PrimaryButton,
AlertModal,
Icon,
} from '@opentrons/components'

import { sendModuleCommand } from '../../robot-api'
import DeckMap from '../DeckMap'
Expand All @@ -21,6 +25,16 @@ function PrepareModules(props: Props) {
const dispatch = useDispatch<Dispatch>()
const [isHandling, setIsHandling] = React.useState(false)

// NOTE: this is the smarter implementation of isHandling that
// relies on the TC reporting its 'in_between' status while the lid m
// motor is moving, which currently doesn't happen because of a FW limitation
// const isHandling = some(
// modules,
// mod => mod.name === 'thermocycler' && mod.data?.lid === 'in_between'
// )

useTimeout(() => setIsHandling(false), isHandling ? LID_OPEN_DELAY_MS : null)

const handleOpenLidClick = () => {
modules
.filter(mod => mod.name === 'thermocycler')
Expand All @@ -32,7 +46,6 @@ function PrepareModules(props: Props) {
)
)
setIsHandling(true)
setTimeout(() => setIsHandling(false), LID_OPEN_DELAY_MS)
}

return (
Expand Down
2 changes: 1 addition & 1 deletion components/src/hooks/__tests__/useInterval.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react'
import { mount } from 'enzyme'
import { useInterval } from '..'

describe('usePrevious hook', () => {
describe('useInterval hook', () => {
const callback = jest.fn()

beforeEach(() => {
Expand Down
51 changes: 51 additions & 0 deletions components/src/hooks/__tests__/useTimeout.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// @flow
import * as React from 'react'
import { mount } from 'enzyme'
import { useTimeout } from '..'

describe('useTimeouthook', () => {
const callback = jest.fn()

beforeEach(() => {
jest.useFakeTimers()
})

afterEach(() => {
jest.resetAllMocks()
jest.clearAllTimers()
jest.useRealTimers()
})

const TestUseTimeout = (props: { delay: number | null }) => {
useTimeout(callback, props.delay)
return <span />
}

test('delay `null` results in no calls', () => {
mount(<TestUseTimeout delay={null} />)
jest.runTimersToTime(10000)

expect(callback).toHaveBeenCalledTimes(0)
})

test('delay sets a timeout', () => {
mount(<TestUseTimeout delay={2} />)
jest.runTimersToTime(3)

expect(callback).toHaveBeenCalledTimes(1)
})

test('re-rendering with delay={null} clears the interval', () => {
const wrapper = mount(<TestUseTimeout delay={4} />)

jest.runTimersToTime(2)
wrapper.setProps({ delay: null })

expect(callback).toHaveBeenCalledTimes(0)

wrapper.setProps({ delay: 4 })
jest.runTimersToTime(6)

expect(callback).toHaveBeenCalledTimes(1)
})
})
1 change: 1 addition & 0 deletions components/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

export * from './usePrevious'
export * from './useInterval'
export * from './useTimeout'
30 changes: 30 additions & 0 deletions components/src/hooks/useTimeout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @flow
import { useEffect, useRef } from 'react'

/**
* React hook to call a function on an interval; copied from:
* https://overreacted.io/making-setinterval-declarative-with-react-hooks/
*
* @template T (type of the input value)
* @param {() => mixed} callback (function to call after timeout)
* @param {number | null} delay (timeout delay, or null to disable the timeout)
* @returns {void}
*/
export function useTimeout(callback: () => mixed, delay: number | null): void {
const savedCallback = useRef()

// remember the latest callback
useEffect(() => {
savedCallback.current = callback
}, [callback])

// set up the interval
useEffect(() => {
const currentCallback = () =>
savedCallback.current && savedCallback.current()
if (delay !== null) {
const id = setTimeout(currentCallback, delay)
return () => clearTimeout(id)
}
}, [delay])
}

0 comments on commit 6fbb842

Please sign in to comment.