jotai
jotai copied to clipboard
`SetStateAction<T>` should accept `T | typeof RESET`
When using atomWithHash or atomWithReset it would be nice if the state setter function would also accept the RESET symbol as return type.
Problem
Suppose I want to encode an isOpen flag into the url hash but also remove it entirely from the URL when it's false.
import { useAtom } from 'jotai'
import { atomWithHash, RESET } from 'jotai/utils'
import React, { useCallback, useMemo } from 'react'
export const useUrlHashToggle = (key: string) => {
const isOpenAtom = useMemo(() => atomWithHash(key, false), [key])
const [isOpen, setIsOpen] = useAtom(isOpenAtom)
const toggle = useCallback(
() => setIsOpen(isOpen ? RESET : true),
[isOpen, setIsOpen]
)
return { isOpen, toggle }
}
const Component: React.FC = () => {
const { isOpen, toggle } = useUrlHashToggle('key')
const expensiveComponents = useMemo(
() => <button onClick={toggle}>{/* expensive stuff */}</button>,
[toggle]
)
return (
<div>
{expensiveComponents}
{isOpen && <div>{/* other stuff */}</div>}
</div>
)
}
As the toggle function depends on isOpen it will change reference when isOpen is toggled. This way expensiveComponents will have to rerender on state change although they don't necessarily depend on isOpen.
Desired Usage
It would be great if it were possible to return RESET in a state setter function:
export const useUrlHashToggle = (key: string) => {
const isOpenAtom = useMemo(() => atomWithHash(key, false), [key])
const [isOpen, setIsOpen] = useAtom(isOpenAtom)
const toggle = useCallback(
() => setIsOpen((prevIsOpen) => prevIsOpen ? RESET : true),
[setIsOpen]
)
return { isOpen, toggle }
}
Now the toggle function would not change reference.
Thanks for suggestion. Yeah, it's probably possible. For background, our original intention was that the symbol is used more internally.
The simple toggle example isn't very convincing because it can be setIsOpen((prev) => !prev).
Do you have another example that you face in practice?
Anyway, do you want to open a PR?
Thanks a lot for the quick answer.
The simple toggle example isn't very convincing because it can be setIsOpen((prev) => !prev).
Well in this case I think it is, as using RESET with atomWithHash removes the entire item from the URL hash.
Suppose in the above example I use useUrlHashToggle('isOpen'):
- The URL hash after
setIsOpen(true)would be#isOpen=true. - After
setIsOpen(false)it would be#isOpen=false. - After
setIsOpen(RESET)it would be#.
The last case is desired as it would be nice to keep the URL hash tidy.
Anyway, do you want to open a PR?
I really would like to, but I currently do not have the time to do so and I'm also going to be on vacation for more than a month. I'll be back in October and I could perhaps do it then.
Alright. If anyone else wants to try, please feel free.
Alright. If anyone else wants to try, please feel free.
Attempted to tackle issue here: https://github.com/pmndrs/jotai/pull/1346
Not sure if typing is optimal, but I hope this is the outcome @V-Mann-Nick desired
@austinwoon Sorry for the late reply. Just checked and I can confirm my use case is perfectly covered with this change. Thanks a lot 👏