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

feat: deprecate some APIs toward v5 #1403

Merged
merged 4 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/previous-versions/zustand-v3-create-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ nav: 18
A special `createContext` is provided since v3.5,
which avoids misusing the store hook.

> **Note**: This function will likely be deprecated in v4 and removed in v5.
> **Note**: This function will be deprecated in v4 and removed in v5.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hey @dai-shi, why is this helper function being deprecated? My team currently relies on this to initialize stores based on data from props.

In preparation for this being deprecated and removed, I've published a package re-creating this helpful function.

Thanks for your help!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this createContext function miss-use React APIs? Or is it a performance aspect? Or do you simply not want to manage this code anymore?

Thank you.

Copy link
Member Author

Choose a reason for hiding this comment

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

In v3, we only have one create function that creates a React hook.
Passing it via context (or props) can violate the rules of hooks. To avoid misusing it, createContext in v3.5 is carefully implemented so that it can't break the rules of hooks.

In v4, we provide a new general hook useStore along with createStore to create a vanilla store without React hooks.
These APIs are more React-ish, and recommended to use over zustand/context workaround. There's no risks to violate the rules of hooks. As we don't want to teach two usages, zustand/context is being deprecated and will be removed in v5.

So, the short answer is it's no longer necessary.

You are absolutely fine to keep it as a third-party package. The helper function could be useful for a pattern.

Copy link
Collaborator

Choose a reason for hiding this comment

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

so are you saying the migration path would be to call createStore and put the result into your own context? Can you maybe show how a replacement of zustand/context would look like?

Copy link
Member Author

@dai-shi dai-shi Jan 10, 2023

Choose a reason for hiding this comment

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

Good point. I answered it in one of discussions, but that should be in docs too.

Copy link

Choose a reason for hiding this comment

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

Would be interested too on how to replace/migrate.
My main usecase is that I need zustand state per instance rather than global.

Copy link
Member Author

Choose a reason for hiding this comment

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


```jsx
import create from 'zustand'
Expand Down
18 changes: 8 additions & 10 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ const unsub1 = useDogStore.subscribe(console.log)
useDogStore.setState({ paw: false })
// Unsubscribe listeners
unsub1()
// Destroying the store (removing all listeners)
useDogStore.destroy()

// You can of course use the hook as you always would
const Component = () => {
Expand Down Expand Up @@ -222,19 +220,21 @@ const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {
Zustand core can be imported and used without the React dependency. The only difference is that the create function does not return a hook, but the API utilities.

```jsx
import create from 'zustand/vanilla'
import createStore from 'zustand/vanilla'

const store = create(() => ({ ... }))
const { getState, setState, subscribe, destroy } = store
const store = createStore(() => ({ ... }))
const { getState, setState, subscribe } = store

export default store
```

You can even consume an existing vanilla store with React:
You can use a vanilla store with `useStore` hook available since v4.

```jsx
import create from 'zustand'
import { useStore } from 'zustand'
import vanillaStore from './vanillaStore'

const useBoundStore = create(vanillaStore)
const useBoundStore = (selector) => useStore(vanillaStore, selector)
Copy link

Choose a reason for hiding this comment

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

it seems that this will require more TS boilerplate

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's just a little. Such usage is already documented somewhere. If people use this pattern a lot, we could provide a util for it. Thanks for your feedback!

```

:warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`.
Expand Down Expand Up @@ -477,8 +477,6 @@ const Component = () => {
...
```

[Alternatively, a special createContext is provided.](./docs/previous-versions/zustand-v3-create-context.md)

## TypeScript Usage

Basic typescript usage doesn't require anything special except for writing `create<State>()(...)` instead of `create(...)`...
Expand Down
8 changes: 8 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ type ExtractState<S> = S extends { getState: () => infer T } ? T : never

type WithoutCallSignature<T> = { [K in keyof T]: T[K] }

/**
* @deprecated Use `createStore` and `useStore` for context usage
*/
function createContext<S extends StoreApi<unknown>>() {
if (__DEV__) {
console.warn(
'[DEPRECATED] zustand/context will be removed in the future version. Please use `import { createStore, useStore } from "zustand"` for context usage. See: https://github.com/pmndrs/zustand/discussions/1180'
)
}
const ZustandContext = reactCreateContext<S | undefined>(undefined)

const Provider = ({
Expand Down
8 changes: 8 additions & 0 deletions src/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,18 @@ type Create = {
<T>(): <Mos extends [StoreMutatorIdentifier, unknown][] = []>(
initializer: StateCreator<T, [], Mos>
) => UseBoundStore<Mutate<StoreApi<T>, Mos>>
/**
* @deprecated Use `useStore` hook to bind store
*/
<S extends StoreApi<unknown>>(store: S): UseBoundStore<S>
}

const createImpl = <T>(createState: StateCreator<T, [], []>) => {
if (__DEV__ && typeof createState !== 'function') {
console.warn(
'[DEPRECATED] Passing a vanilla store will be unsupported in the future version. Please use `import { useStore } from "zustand"` to use the vanilla store in React.'
)
}
const api =
typeof createState === 'function' ? createStore(createState) : createState

Expand Down
13 changes: 12 additions & 1 deletion src/vanilla.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ export interface StoreApi<T> {
setState: SetStateInternal<T>
getState: () => T
subscribe: (listener: (state: T, prevState: T) => void) => () => void
/**
* @deprecated Use `unsubscribe` returned by `subscribe`
*/
destroy: () => void
}

Expand Down Expand Up @@ -85,7 +88,15 @@ const createStoreImpl: CreateStoreImpl = (createState) => {
return () => listeners.delete(listener)
}

const destroy: StoreApi<TState>['destroy'] = () => listeners.clear()
const destroy: StoreApi<TState>['destroy'] = () => {
if (__DEV__) {
console.warn(
'[DEPRECATED] The destroy method will be unsupported in the future version. You should use unsubscribe function returned by subscribe. Everything will be garbage collected if store is garbage collected.'
)
}
listeners.clear()
}

const api = { setState, getState, subscribe, destroy }
state = createState(setState, getState, api)
return api as any
Expand Down