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

next js example has typescript error #493

Closed
hemedani opened this issue Jul 12, 2021 · 11 comments
Closed

next js example has typescript error #493

hemedani opened this issue Jul 12, 2021 · 11 comments
Labels
typescript This issue is about TypeScript

Comments

@hemedani
Copy link

this code guide to set type of useStore to UseStore<typeof initialState> with this hint :

const zustandContext = createContext()
export const Provider = zustandContext.Provider
// An example of how to get types
/** @type {import('zustand/index').UseStore<typeof initialState>} */
export const useStore = zustandContext.useStore

but it's throw this error

Type 'UseStoreData<object>' is missing the following properties from type 'UseStore<{ blogFirstPage: BlogFirstPage; }>': setState, getState, subscribe, destroyts(2739)

and this chunk of code :

    useLayoutEffect(() => {
      if (initialState && store) {
        store.setState({
          ...store.getState(),
          ...initialState,
        })
      }
    }, [initialState])

throw this error :

This condition will always return true since this function is always defined. Did you mean to call it instead?

we should call store on condition

    useLayoutEffect(() => {
      if (initialState && store()) {
        store.setState({
          ...store.getState(),
          ...initialState,
        })
      }
    }, [initialState])
@dai-shi
Copy link
Member

dai-shi commented Jul 12, 2021

@Munawwar can you figure it out?

@hemedani
Copy link
Author

hemedani commented Jul 14, 2021

the second error fixed if defined store undefined-able like this :

let store: UseStore<InitialState> | undefined;

and this code is fine know

    useLayoutEffect(() => {
      if (initialState && store) {
        store.setState({
          ...store.getState(),
          ...initialState,
        })
      }
    }, [initialState])

@hemedani
Copy link
Author

when removing type from useStore :

export const useStore = zustandContext.useStore;

throw this error

Exported variable 'useStore' has or is using name 'UseStoreData' from external module "/Users/syd/work/newKaryan/node_modules/zustand/context" but cannot be named.

and on declaration of createContext useStore defiend like this

declare function createContext<TState extends State>(): {
    Provider: ({ initialStore, createStore, children, }: {
        /**
         * @deprecated
         */
        initialStore?: UseStore<TState> | undefined;
        createStore: () => UseStore<TState>;
        children: ReactNode;
    }) => import("react").FunctionComponentElement<import("react").ProviderProps<UseStore<TState> | undefined>>;
    useStore: UseStoreData<TState>;
    useStoreApi: () => {
        getState: import("zustand").GetState<TState>;
        setState: import("zustand").SetState<TState>;
        subscribe: import("zustand").Subscribe<TState>;
        destroy: import("zustand").Destroy;
    };
};

and the type of it it's not exported

interface UseStoreData<T extends State> {
    (): T;
    <U>(selector: StateSelector<T, U>, equalityFn?: EqualityChecker<U>): U;
}

I'm trying to redefine interface on my files but it's throw this error

Type 'UseStoreData<object>' is not assignable to type 'UseStoreData<InitialState>'.
  Property 'blogFirstPage' is missing in type '{}' but required in type 'InitialState'.

but fixed when i set generic type of createContext();

const zustandContext = createContext<InitialState>();

@hemedani
Copy link
Author

know everything is fixed and the whole line of the code is this

import { useLayoutEffect } from "react";
import create, {
  EqualityChecker,
  State,
  StateSelector,
  UseStore,
} from "zustand/index";
import createContext from "zustand/context";
import {
  BlogFirstPage,
  blogFirstPageInitial,
  blogFirstPageState,
} from "../index";

interface UseStoreData<T extends State> {
  (): T;
  <U>(selector: StateSelector<T, U>, equalityFn?: EqualityChecker<U>): U;
}

interface InitialState {
  blogFirstPage: BlogFirstPage;
}

const initialState: InitialState = {
  blogFirstPage: blogFirstPageInitial,
};

let store: UseStore<InitialState> | undefined;

const zustandContext = createContext<InitialState>();
export const Provider = zustandContext.Provider;
// An example of how to get types
/** @type {import('zustand/index').UseStore<typeof initialState>} */
export const useStore: UseStoreData<InitialState> = zustandContext.useStore;

export const initializeStore = (preloadedState = {}) => {
  return create<typeof initialState>(blogFirstPageState(preloadedState));
};

export function useHydrate(initialState: InitialState) {
  let _store = store ?? initializeStore(initialState);

  // For SSR & SSG, always use a new store.
  if (typeof window !== "undefined") {
    // For CSR, always re-use same store.
    if (!store) {
      store = _store;
    }

    // And if initialState changes, then merge states in the next render cycle.
    //
    // eslint complaining "React Hooks must be called in the exact same order in every component render"
    // is ignorable as this code runs in the same order in a given environment (CSR/SSR/SSG)
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useLayoutEffect(() => {
      if (initialState && store) {
        store.setState({
          ...store.getState(),
          ...initialState,
        });
      }
    }, [initialState]);
  }

  return _store;
}

@dai-shi
Copy link
Member

dai-shi commented Jul 14, 2021

Please try v3.5.7 if you haven't, to see if things change.

@hemedani
Copy link
Author

yes it fixed

@hemedani
Copy link
Author

it has another type error on Provider :

import { useHydrate, Provider } from '../lib/store'

export default function App({ Component, pageProps }) {
  const store = useHydrate(pageProps.initialZustandState)
  return (
    <Provider initialStore={store}>
      <Component {...pageProps} />
    </Provider>
  )
}

it's throw this error

Property 'createStore' is missing in type '{ children: Element; initialStore: UseStore<InitialState>; }' but required in type '{ initialStore?: UseStore<InitialState>; createStore: () => UseStore<InitialState>; children: ReactNode; }'.ts(2741)

I think createStore should be null-able

@hemedani
Copy link
Author

or just change code to this one :

export default function App({ Component, pageProps }) {
  const store = () => useHydrate(pageProps.initialZustandState)
  return (
    <Provider createStore={store}>
      <Component {...pageProps} />
    </Provider>
  )
}

@dai-shi
Copy link
Member

dai-shi commented Jul 16, 2021

Yeah, initialStore is deprecated.

useHydrate should be called in App, so maybe this:

export default function App({ Component, pageProps }) {
  const store = useHydrate(pageProps.initialZustandState)
  return (
    <Provider createStore={() => store}>
      <Component {...pageProps} />
    </Provider>
  )
}

@Munawwar
Copy link
Contributor

@Munawwar can you figure it out?

Sorry for the late reply. Didnt see this whole conversation. I opened a PR with next.js to update that example.

Latest code you can see at https://github.com/Munawwar/next.js-1/tree/zustand-example and https://codesandbox.io/s/nextjs-with-zustand-354-forked-bryjr?file=/lib/store.js

@dai-shi dai-shi added the typescript This issue is about TypeScript label Jul 22, 2021
@dai-shi
Copy link
Member

dai-shi commented Jul 22, 2021

See vercel/next.js#27229

@dai-shi dai-shi closed this as completed Jul 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
typescript This issue is about TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants