Dynalon / reactive-state

Redux-clone build with strict typing and RxJS down to its core. Wrist-friendly, no boilerplate or endless switch statements
MIT License
138 stars 7 forks source link

Hooks based access to state? #24

Closed bytenik closed 4 years ago

bytenik commented 4 years ago

Are you open to hooks-based access to state if I code it and submit a PR?

Something similar to how react-router has withRouter akin to connect, but also now has useRouteMatch / useHistory / etc. etc.

Dynalon commented 4 years ago

Hi bytenik!

Yes, I am definitely open to hooks, and inside the react-bridge there is already a useStore() hook to obtain a copy of the store. But you are probably looking for a useState() hook that returns the state right away? I'd of course like that too so yes please send a PR 👍

Out of my head I'd guess the signature would look something like:

function useState<TState = object, TSlice = TState>(projection?: (state: TState) => TSlice) { /* ... */ }

// possible usages:

// will not be very typesafe, and re-render on *every* action :(
const state = useState()

// better typing, still slow due to rerender on every action
const state = useState<MyStateType>()

// should auto-infer the type of the returned value to number
// providing a projection function will only trigger re-render if the shallow-equality test failed (=there is a real change)
const slice = useState<MyStateType>(state => state.counter)

What do you think?

bytenik commented 4 years ago

That's exactly what I was thinking. Probably need to leave room for action maps too, similar to how the existing connect function works.

bytenik commented 4 years ago

Actually, I started working on it and the action map functionality felt very out of place. It was one thing when it was being used as a prop, but, in the hooks-based pattern it felt odd to take a reference to a subject but then to basically wave hands and pretend it was a function. If someone really wants to do that, they could easily have a separate non-hook like subjectAsFunction<T>(subject: Subject<T>): (value: T) => void to make that happen for them, which would be a one-liner wrapper around .next.

bytenik commented 4 years ago

By the way it occurs to me after I made it that useState may be occupied by React. 😁 🙃

I'm going with useStoreState.

bytenik commented 4 years ago

Unfortunately, due to https://github.com/microsoft/TypeScript/issues/26242 still being open and not implemented, your third proposed use case with type inference for the state slice is not possible. The only way to get that level of inference is through a fluent-ish interface, which I implemented as well even though it feels a bit kludgy:

const slice = useSlicer<TestState>()(({message}) => ({ message }));

bytenik commented 4 years ago

See PR https://github.com/Dynalon/reactive-state/pull/25

bytenik commented 4 years ago

Thank you for merging the PR. Can you push out a new version to npm? Thanks!

Dynalon commented 4 years ago

This is now published in v3.6.0, thanks for your help 👍