question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support non-object global states.

See original GitHub issue

Something 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:

image

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:closed
  • Created 5 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
CharlesStovercommented, Mar 27, 2019

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 use createProvider({ 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 support setGlobal(1) and instead must be setGlobal({ count: 1 }), but other than that, you are still able to segment this part of the state from the rest and use Provider.useGlobal() to access it as a hook.

With non-object states, you could do:

// state.ts
import { createProvider } from 'reactn';
export default createProvider(0);
// components.tsx
import { useGlobal } from './state';

export const Increaser = () => {
  const [count, setCount] = useGlobal();
  return (<>
    <div>Counter is {count}</div>
    <button onClick={() => setCount(count + 5)}>add five</button>
  </>)
}

export const Decreaser = () => {
  const [count, setCount] = useGlobal();
  return (<>
    <div>Counter is {count}</div>
    <button onClick={() => setCount(count - 10)}>subtract ten</button>
  </>)
}

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.

1reaction
mmkalcommented, Mar 5, 2019

I do think it is interesting to be able to automatically infer types like this, but it isn’t fool-proof. If I call setGlobal in two different components, they won’t share the inferred type, even though they will share the state.

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 call setGlobal only once. Anyone who wants to stray from the recommendation can cast to any in typescript, and do whatever weird stuff they want from there.

If setGlobal were to return anything, I think it would be a Promise for asynchronous state management.

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 with useGlobal, so will yield a value and cause a render whenever it’s ready.

I often recommend [calling setGlobal before useGlobal], but I don’t think of it as a requirement. useGlobal(‘cards’) returning undefined makes sense to me, the same way anyObject.cards returning undefined makes sense.

Maybe the existing useGlobal import could be typed as UseGlobal<Record<string, undefined>>, so if people really want to start from no state at all, they can, and useGlobal('cards') would be of type undefined as expected. My personal opinion on this is to just to recommend setGlobal({}) 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).

[multiple state objects] works for hooks, but it doesn’t help class Components

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 dropped Components completely over supporting the two of them with a more limited feature set.

Read more comments on GitHub >

github_iconTop Results From Across the Web

You Can Definitely Use Global Variables To Manage Global ...
With this and a little help from hooks, we'll be able to manage global state completely with global variables.
Read more >
Global Variables and States: Why So Much Hate?
Global mutable states, by definition, are available everywhere, which is the contrary of encapsulation! No access restriction whatsoever.
Read more >
Why is Global State so Evil?
If a particular object needs a particular state, then it should explicitly ask for it by ... Mutable global state is evil for...
Read more >
How do you guys manage "global" state? : r/cpp - Reddit
How do you guys manage "global" state? · The global class takes care of construction and destruction, thus fixing the construction/destruction ...
Read more >
Preserving global state in a flask application - Stack Overflow
As far as I understand it, the Application Context, in particular the flask.g object should be used for this. Setup: import flask as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found