Skip to content
This repository has been archived by the owner on Jan 2, 2021. It is now read-only.

Commit

Permalink
fix: useSafeState
Browse files Browse the repository at this point in the history
  • Loading branch information
tannerlinsley committed Aug 31, 2020
1 parent 721b7e6 commit 7b90647
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 7 deletions.
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"workbench.colorCustomizations": {
"titleBar.activeBackground": "#ff4154", // change this color!
"titleBar.inactiveBackground": "#ff4154", // change this color!
"titleBar.activeBackground": "#00ac4e", // change this color!
"titleBar.inactiveBackground": "#00ac4e", // change this color!
"titleBar.activeForeground": "#ffffff", // change this color!
"titleBar.inactiveForeground": "#ffffff" // change this color!
}
Expand Down
11 changes: 6 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import match from 'match-sorter'
import { queryCache as cache, useQueryCache } from 'react-query'
import useLocalStorage from './useLocalStorage'
import { useSafeState } from './utils'

//

Expand Down Expand Up @@ -67,11 +68,11 @@ export function ReactQueryDevtools({
'reactQueryDevtoolsOpen',
initialIsOpen
)
const [isResolvedOpen, setIsResolvedOpen] = React.useState(false)
const [isResolvedOpen, setIsResolvedOpen] = useSafeState(false)

React.useEffect(() => {
setIsResolvedOpen(isOpen)
}, [isOpen, isResolvedOpen])
}, [isOpen, isResolvedOpen, setIsResolvedOpen])

React[isServer ? 'useEffect' : 'useLayoutEffect'](() => {
if (isResolvedOpen) {
Expand Down Expand Up @@ -241,7 +242,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef(
false
)

const [isDragging, setIsDragging] = React.useState(false)
const [isDragging, setIsDragging] = useSafeState(false)

const sortFn = React.useMemo(() => sortFns[sort], [sort])

Expand Down Expand Up @@ -281,7 +282,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef(
setIsDragging(false)
}

const [unsortedQueries, setUnsortedQueries] = React.useState(
const [unsortedQueries, setUnsortedQueries] = useSafeState(
Object.values(queryCache.queries)
)

Expand Down Expand Up @@ -321,7 +322,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef(
return queryCache.subscribe(queryCache => {
setUnsortedQueries(Object.values(queryCache.queries))
})
}, [sort, sortFn, sortDesc, queryCache])
}, [sort, sortFn, sortDesc, queryCache, setUnsortedQueries])

return (
<ThemeProvider theme={theme}>
Expand Down
53 changes: 53 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react'
import { useTheme } from './theme'
import useMediaQuery from './useMediaQuery'

export const isServer = typeof window === 'undefined'

export function getQueryStatusColor(query, theme) {
return query.state.isFetching
? theme.active
Expand Down Expand Up @@ -54,3 +56,54 @@ export function styled(type, newStyles, queries = {}) {
})
})
}

function useIsMounted() {
const mountedRef = React.useRef(false)
const isMounted = React.useCallback(() => mountedRef.current, [])

React[isServer ? 'useEffect' : 'useLayoutEffect'](() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])

return isMounted
}

/**
* This hook is a safe useState version which schedules state updates in microtasks
* to prevent updating a component state while React is rendering different components
* or when the component is not mounted anymore.
*/
export function useSafeState(initialState) {
const isMounted = useIsMounted()
const [state, setState] = React.useState(initialState)

const safeSetState = React.useCallback(
value => {
scheduleMicrotask(() => {
if (isMounted()) {
setState(value)
}
})
},
[isMounted]
)

return [state, safeSetState]
}

/**
* Schedules a microtask.
* This can be useful to schedule state updates after rendering.
*/
function scheduleMicrotask(callback) {
Promise.resolve()
.then(callback)
.catch(error =>
setTimeout(() => {
throw error
})
)
}

0 comments on commit 7b90647

Please sign in to comment.