Support non-object global states.
See original GitHub issueSomething that would help with typescript/flow typings, as well as make it easier to use the package correctly - instead of hinting that global state should be initialised before ever calling useGlobal
, you could be sure that setGlobal
had been called, if that were the recommended/only way to get the useGlobal
reference:
// state.js
import { setGlobal } from 'reactn';
export const useGlobal = setGlobal({
cards: [],
disabled: false,
initial: 'values',
x: 1
});
// index.js
import ReactDOM from 'react-dom';
import App from './App';
// no need to call `useGlobal` here:
// it'll be called when the module is `require(...)`d for the first time.
ReactDOM.render(
<App />,
document.getElementById('root')
);
// SomeComponent.js
import { useGlobal } from './state'
export const SomeComponent = () => {
const [state, setGlobal] = useGlobal()
return <div>{state.cards.join(', ')}</div>
}
In index.d.ts
, setGlobal
could be typed with a generic to so useGlobal
could also have useful types (right now, { [property: string]: any }
which allows any property access with no restrictions and no hints):
export function setGlobal<State>(initialState: State): UseGlobal<State>
interface UseGlobal<State> {
(): UseGlobalResult<State>
<Key extends keyof State>(key: Key): UseGlobalResult<State[Key]>
}
type UseGlobalResult<T> = [T, (newState: T) => void]
You then get meaningful autocomplete out-of-the-box. This would work even in javascript in most IDEs, because the type passed in to setGlobal
can be inferred. Example:
Here the compiler finds the bug - that we’re trying to pass in a number to cards
, which is an array of strings.
One other possible benefit (although possibly not initially; it would depend on the implementation) is that by being more functional - returning a function rather than relying on side-effects - you could conceivably have multiple state objects that can span components.
What do you think? If you’d be open to the change I could help with a PR.
Issue Analytics
- State:
- Created 5 years ago
- Comments:7 (4 by maintainers)
I’m revamping this issue, if it’s okay, to change state behavior to support non-object types. I think the closest alignment to the original request that I am willing to support is
createProvider(0)
. You can usecreateProvider({ count: 0 })
in ReactN already to generate a state and associated reducers that you can import and use in your Components. The only difference is it won’t supportsetGlobal(1)
and instead must besetGlobal({ count: 1 })
, but other than that, you are still able to segment this part of the state from the rest and useProvider.useGlobal()
to access it as a hook.With non-object states, you could do:
I believe this is exactly the same as your last comment, except you don’t have to wrap provider around your app because it isn’t using Context.
Non-object states aren’t planned for the 2.0 milestone, but I toyed with and support them. They aren’t on the roadmap because it was difficult to get them to play well with TypeScript. Once 2.0 launches, it may be easier to revisit the issue in a codebase that is 100% TypeScript.
That’s true, even if ideally it wouldn’t be. It’s also true right now already. If you call
setGlobal
in two places it’ll result in confusing behaviour based on import order because they’ll fight with each other over the global state. Under this proposal the types would then also be confusing, reflecting that runtime ambiguity. If multiple state objects can’t work, then this could just become a “don’t do that” situation. i.e. some documentation recommending users to callsetGlobal
only once. Anyone who wants to stray from the recommendation can cast toany
in typescript, and do whatever weird stuff they want from there.That would definitely make things more complex in terms of the usage and the types. But given
setGlobal
returns a hook function, might it be ok to return the function synchronously, even if the data won’t arrive until later? The data should always be fetched withuseGlobal
, so will yield a value and cause a render whenever it’s ready.Maybe the existing
useGlobal
import could be typed asUseGlobal<Record<string, undefined>>
, so if people really want to start from no state at all, they can, anduseGlobal('cards')
would be of typeundefined
as expected. My personal opinion on this is to just to recommendsetGlobal({})
instead if you want an empty initial state, so that there aren’t many ways of doing the same thing. It’d make the library simpler. It’s easier to reason about too, like the difference between an empty state (state = {}
) and an undefined state (state = undefined
).Fair enough. I found this library because I wanted a cleaner alternative to redux that used react hooks, so my initial assumption was that this library would only support hooks. I’m definitely biased because of that, but I’d love it if
reactn
focused only on hooks, and droppedComponents
completely over supporting the two of them with a more limited feature set.