Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: creates persisted store for Active Project Id using Zustand #67

Merged
merged 13 commits into from
Dec 19, 2024

Conversation

ErikSin
Copy link
Contributor

@ErikSin ErikSin commented Dec 17, 2024

  • Creates utility function for zustand store to create reusable hooks
  • Creates activeProjectIdStore.
  • Reorganizes providers to sir outside the Router (and created an App.tsx file to hold providers)

@ErikSin ErikSin requested a review from gmaclennan December 17, 2024 01:45
Comment on lines 51 to 54
function useActions() {
const context = useCurrentContext()
return context.actions
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im having trouble with this typing. Im not able to get the actions back, any chance you see the problem?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd need more time to try to figure it out. My gut feeling is that attempting to create a helper function for creating a store and provider is a "premature abstraction", by which I mean an abstraction made before being clear about how something is going to be re-used, and being clear about the goal of the abstraction. I assume the goal is to avoid repeating code for each store, but the costs are:

  • More complicated code and typings
  • The need to change things in many places when you want to add additional functionality to a store, like configuring migrations
  • More code to test and maintain

I'm not sure it really saves that much code or makes it easier to understand what is happening vs. just following the pattern with some repeated code each time. Doing it without the helper will certainly make the types easier. I imagine the issue is related to the wrapping of actions and generics, but it would take me an hour or so to figure it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that there is a lot of boiler plate code and we want all our stores to look the same. Also I am anticipating that we are going to have a few of these stores as In mobile we have quite a few persisted store. I think there will be slightly less for desktop (we don't need a persisted observation store for example). But I think there are still going to be a few stores.

We are expecting all of them to return:

  1. a provider, that takes the value as props
  2. a createStore function that returns a persisted or non persisted store
  3. a useStore hook (with atomic selectors)
  4. a useStoreAction hook. (we also do not want the actions to be in the zustand store).

We have a lot of very specific requirements for these stores. So it feels like there is alot of room for error by not abstracting this?

Perhaps it would be best to abstract some of these things, but not all of them? Or do you think abstraction is not necessary at all?

Copy link
Member

@gmaclennan gmaclennan Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to know exactly what makes sense to abstract until you have started writing some actual stores. I think it's generally fine to start with repeated boilerplate. The challenge with writing the abstractions first is that you can end up trapping yourself in a corner with abstractions that don't actually make sense or make the code harder to write, or you find that every change requires changing lots of pieces of the abstraction. This is very similar to the guidelines we have talked about with React components - it's best to abstract small re-usable parts of the code, rather than creating a large abstraction (component) that tries to manage lots of different things, and ends up adding to the maintenance burden.

An example here might be a future need to have a Zustand store that is not / should not be persisted, and then having to adjust the abstractions so that they can handle that case.

Looking at this code, I think the most "boiler-platey" part which would be repeated and is unlikely to change is the hooks, so you could create an abstraction like:

function createHooks<TStoreState, TStoreActions>(
  context: React.Context<{
    store: StoreApi<TStoreState>;
    actions: TStoreActions;
  }>,
) {
  function useContextValue() {
    const value = useContext(context);
    if (!value) {
      throw new Error('Must set up the provider first');
    }
    return value;
  }

  function useStoreState(): TStoreState;
  function useStoreState<S>(selector: (state: TStoreState) => S): S;
  function useStoreState<S>(selector?: (state: TStoreState) => S) {
    const {store} = useContextValue();
    return useStore(store, selector!);
  }

  function useStoreActions() {
    const {actions} = useContextValue();
    return actions;
  }

  return {useStoreState, useStoreActions};
}

That feels like something that is unlikely to change and is fairly simple to understand and test (if tests are needed), and is limited in scope about what it is trying to do (takes a single argument and turns it into two hooks)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<CssBaseline />
<IntlProvider>
<QueryClientProvider client={queryClient}>
<PersistedActiveProjectIdProvider store={PersistedProjectIdStore}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: this is really <ActiveProjectIdProvider> - the provider is the same whether it is persisted or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}

const PersistedProjectIdStore = createActiveProjectIdStore({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: we haven't discussed this, but generally we've followed the pattern of naming instances with lowercase, and factory functions / classes with capitalized names, I think it's good to continue that pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ErikSin ErikSin requested a review from achou11 December 18, 2024 18:41
@ErikSin ErikSin changed the title Es/add zustand store chore: creates persisted store for Active Project Id using Zustand Dec 18, 2024
Copy link
Member

@gmaclennan gmaclennan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, looks good.

Comment on lines 21 to 22

console.log({ activeProjectId })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log({ activeProjectId })

}
}

const PersistedProjectIdStore = CreateActiveProjectIdStore({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const PersistedProjectIdStore = CreateActiveProjectIdStore({
const persistedProjectIdStore = createActiveProjectIdStore({

I think my comment before was maybe not very clear, I did not mean for you to change the case of CreateACtiveProjectIdStore

@awana-lockfile-bot
Copy link

package-lock.json changes

Click to toggle table visibility
Name Status Previous Current
zustand ADDED - 5.0.2

@ErikSin ErikSin merged commit 2a8d85b into main Dec 19, 2024
4 checks passed
@ErikSin ErikSin deleted the es/add-zustand-store branch December 19, 2024 04:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants