跳到主要内容

useStore

useStore is a React hook that returns the state of a registered store. It finds the nearest StoreProvider in the component tree that contains the specified hook and returns the hook's state from the provider.

useStore<S>(hook: StoreHook<S>): S;
useStore<S, T>(hook: StoreHook<S>, selector: (state: S) => T): T;
useStore<S, T>(hook: StoreHook<S>, selector: (state: S) => T, isEqual: ((current: T, next: T) => boolean)): T;

Parameters​

  • hook: The hook registered as a store.
  • selector: A function to select a specific value from the store's state, allowing you to control re-renders for better performance.
  • isEqual: A function to compare two values and determine equality. If not provided, shallow comparison is used by default.

Returns​

useStore returns the state directly if no selector is specified; otherwise, it returns the value produced by the selector.

Usage​

Let's create a useProduct hook to use as a store and add the provider at the root of the app.

useProduct.ts
import { useState } from "react";

export default function useProduct() {
const [price, setPrice] = useState(5);
const [count, setCount] = useState(100);

return {
price,
count,
setPrice,
setCount,
};
}
provider.ts
import useProduct from "./useProduct";
import { createProvider } from "houp";

export const Provider = createProvider([useProduct]);
index.tsx
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import App from "./App"
import { Provider } from "./provider";

createRoot(document.getElementById("root")!).render(
<StrictMode>
<Provider>
<App />
</Provider>
</StrictMode>,
)

useStore​

useStore fetches all the data from the useProduct store, causing the component to re-render on every state change. To re-render the component only when specific state values like count or price change, you should use useStore with a selector.

Product.ts
import { useStore } from "houp";
import useProduct from "./useProduct";

export function Product() {
const store = useStore(useProduct);

return (
<>
<div>count: {store.count}</div>
</>
);
}

Using useStore with a selector​

useStore supports a selector argument. The selector allows you to choose specific state from the store, so the component will only re-render when the selected state changes.

Product.ts
import { useStore } from "houp";
import useProduct from "./useProduct";

export function Product() {
const store = useStore(useProduct, s => ({ count: s.count }));

return (
<>
<div>count: {store.count}</div>
</>
);
}

Specify your own isEqual function​

You can also provide your own isEqual function to compare two values and determine equality. If not provided, shallow comparison is used by default.

Product.ts
import { useStore } from "houp";
import useProduct from "./useProduct";

export function Product() {
const store = useStore(useProduct, s => ({ count: s.count }), (current, next) => next.count <= 110);

return (
<>
<div>count: {store.count}</div>
</>
);
}
信息

This component will only re-render when the count > 110.

Troubleshooting​

I got an error: Unable to find store. This usually occurs when the StoreProvider is not added to the App.​

This usually happens when you call useStore, but you have not added any provider to the component tree.

// cannot find useProduct store in the component tree.
const state = useStore(useProduct);

Create a provider with useProduct will fix the issue.

provider.ts
import useProduct from "./useProduct";
import { createProvider } from "houp";

export const Provider = createProvider([useProduct]);
index.tsx
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import App from "./App"
import { Provider } from "./provider";

createRoot(document.getElementById("root")!).render(
<StrictMode>
<Provider>
<App />
</Provider>
</StrictMode>,
)

I got an error: Unable to find store. Did you forget to add it when calling createProvider?​

This usually happens when you call useStore, but we cannot find it in the provider.

// cannot find useProduct store in the provider.
const state = useStore(useProduct);

Make sure to add useProduct as a parameter when creating the provider.

provider.ts
import useProduct from "./useProduct";
import { createProvider } from "houp";

export const Provider = createProvider([useProduct]);