Skip to content

Commit afc1a47

Browse files
Add documentation for useSharedStateSelector hook and its usage examples
1 parent 87d8eb8 commit afc1a47

File tree

1 file changed

+93
-43
lines changed

1 file changed

+93
-43
lines changed

README.md

Lines changed: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -209,52 +209,90 @@ export default function App(){
209209
| Static/shared creation | Use `createSharedState`, `createSharedFunction`, `createSharedSubscription` to export reusable, type-safe shared resources that persist across `clearAll()` calls. |
210210

211211

212-
## 🏗️ Sharing State (`useSharedState`)
213-
Signature:
214-
- `const [value, setValue] = useSharedState(key, initialValue, scopeName?)`
215-
- `const [value, setValue] = useSharedState(sharedStateCreated)`
212+
## Selecting State Slices (`useSharedStateSelector`)
216213

217-
Behavior:
218-
* First hook call (per key + scope) seeds with `initialValue`.
219-
* Subsequent mounts with same key+scope ignore their `initialValue` (consistent source of truth).
220-
* Setter accepts either value or updater `(prev)=>next`.
221-
* React batching + equality check: listeners fire only when the value reference actually changes.
222-
* States created with `createSharedState` are **static** by default and are not removed by `clear()` or `clearAll()`, ensuring they persist.
214+
When a shared state holds an object, you might only need a small piece of it.
215+
Using `useSharedState` will cause your component to re-render whenever *any* part of the object changes.
216+
To optimize performance and avoid unnecessary re-renders, you can use the `useSharedStateSelector` hook.
223217

224-
### Examples
225-
1. Global theme (recommended for large apps)
226-
```tsx
227-
// themeState.ts
228-
export const themeState = createSharedState('light');
229-
// In components
230-
const [theme, setTheme] = useSharedState(themeState);
231-
```
232-
2. Isolated wizard progress
233-
```tsx
234-
const wizardProgress = createSharedState(0);
235-
<SharedStatesProvider>
236-
<Wizard/>
237-
</SharedStatesProvider>
238-
// In Wizard
239-
const [step, setStep] = useSharedState(wizardProgress);
240-
```
241-
3. Forcing crossportal sync
242-
```tsx
243-
const navState = createSharedState('closed', 'nav');
244-
<SharedStatesProvider scopeName="nav" children={<PrimaryNav/>} />
245-
<Portal>
246-
<SharedStatesProvider scopeName="nav" children={<MobileNav/>} />
247-
</Portal>
248-
// In both navs
249-
const [navOpen, setNavOpen] = useSharedState(navState);
250-
```
251-
4. Overriding nearest provider
252-
```tsx
253-
// Even if inside a provider, this explicitly binds to global
254-
const globalFlag = createSharedState(false, '_global');
255-
const [flag, setFlag] = useSharedState(globalFlag);
256-
```
218+
This hook allows you to subscribe to a specific, memoized slice of a shared state.
219+
Your component will only re-render if the selected value changes.
220+
It uses `react-fast-compare` for efficient deep equality checks.
221+
222+
Signature:
223+
- `const selectedValue = useSharedStateSelector(key, selector, scopeName?)`
224+
- `const selectedValue = useSharedStateSelector(sharedStateCreated, selector)`
225+
226+
The `selector` is a function that receives the full state and returns the desired slice.
227+
228+
### Example: Subscribing to a slice of a user object
229+
230+
Imagine a shared state for user settings:
257231

232+
```tsx
233+
// settingsState.ts
234+
import { createSharedState } from 'react-shared-states';
235+
236+
export const settingsState = createSharedState({
237+
theme: 'dark',
238+
notifications: {
239+
email: true,
240+
push: false,
241+
},
242+
language: 'en',
243+
});
244+
```
245+
246+
A component that only cares about the theme can use `useSharedStateSelector` to avoid re-rendering when, for example, the notification settings change.
247+
248+
```tsx
249+
import { useSharedState, useSharedStateSelector } from 'react-shared-states';
250+
import { settingsState } from './settingsState';
251+
252+
function ThemeDisplay() {
253+
const theme = useSharedStateSelector(settingsState, (settings) => settings.theme);
254+
255+
console.log('ThemeDisplay renders'); // This will only log when the theme changes
256+
257+
return <div>Current theme: {theme}</div>;
258+
}
259+
260+
function NotificationToggle() {
261+
const [settings, setSettings] = useSharedState(settingsState);
262+
263+
const togglePush = () => {
264+
setSettings(s => ({
265+
...s,
266+
notifications: { ...s.notifications, push: !s.notifications.push }
267+
}));
268+
};
269+
270+
return <button onClick={togglePush}>Toggle Push Notifications</button>;
271+
}
272+
```
273+
274+
In this example, clicking the `NotificationToggle` button will **not** cause `ThemeDisplay` to re-render.
275+
276+
### A Note on Type Safety
277+
278+
For the best developer experience and full type safety,
279+
it is **highly recommended** to use `useSharedStateSelector` with a statically created shared state object from `createSharedState`.
280+
281+
When you use a string key directly, the hook cannot infer the type of the state object.
282+
You would have to provide the types explicitly as generic arguments, or they will default to `any`.
283+
284+
```tsx
285+
// Less safe: using a string key
286+
// You have to specify the types manually.
287+
const theme = useSharedStateSelector<{ theme: string; /*...other props*/ }, 'settings', string>(
288+
'settings',
289+
(settings) => settings.theme
290+
);
291+
292+
// Recommended: using a created state object
293+
// Types are inferred automatically!
294+
const theme = useSharedStateSelector(settingsState, (settings) => settings.theme);
295+
```
258296

259297
## ⚡ Shared Async Functions (`useSharedFunction`)
260298
Signature:
@@ -547,6 +585,12 @@ Returns `[value, setValue]`.
547585
### `useSharedState(sharedStateCreated)`
548586
Returns `[value, setValue]`.
549587

588+
### `useSharedStateSelector(key, selector, scopeName?)`
589+
Returns the selected value.
590+
591+
### `useSharedStateSelector(sharedStateCreated, selector)`
592+
Returns the selected value.
593+
550594
### `useSharedFunction(key, fn, scopeName?)`
551595
Returns `{ state, trigger, forceTrigger, clear }`.
552596

@@ -924,6 +968,12 @@ Returns `[value, setValue]`.
924968
### `useSharedState(sharedStateCreated)`
925969
Returns `[value, setValue]`.
926970

971+
### `useSharedStateSelector(key, selector, scopeName?)`
972+
Returns the selected value.
973+
974+
### `useSharedStateSelector(sharedStateCreated, selector)`
975+
Returns the selected value.
976+
927977
### `useSharedFunction(key, fn, scopeName?)`
928978
Returns `{ state, trigger, forceTrigger, clear }`.
929979

0 commit comments

Comments
 (0)