### Start Development Server
Source: https://github.com/lostpebble/pullstate/blob/master/website/README.md
Use this command to start the Docusaurus development server and preview your site.
```sh
yarn start
```
--------------------------------
### Install Website Dependencies
Source: https://github.com/lostpebble/pullstate/blob/master/website/README.md
Run this command to install all necessary dependencies for the Docusaurus website.
```sh
yarn
```
--------------------------------
### Install Pullstate
Source: https://context7.com/lostpebble/pullstate/llms.txt
Install the Pullstate library using yarn or npm.
```bash
yarn add pullstate
# or
npm install pullstate
```
--------------------------------
### Async Action Example (JavaScript)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
Example of an async action in JavaScript that fetches pictures based on a tag. It uses `successResult` and `errorResult` to handle the outcome and `useBeckon` to get the state in a component.
```tsx
import { createAsyncAction, errorResult, successResult } from "pullstate";
const searchPicturesForTag = createAsyncAction(async ({ tag }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
return successResult(result.pictures);
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
});
export const PictureExample = props => {
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
if (!finished) {
return
Loading Pictures for tag "{props.tag}"
;
}
if (result.error) {
return
{result.message}
;
}
return ;
};
```
--------------------------------
### Example: Batch Multiple Updates
Source: https://github.com/lostpebble/pullstate/blob/master/docs/update-store.md
Shows how to batch multiple, modular updates to the `UIStore` by passing an array of updater functions to the `update()` method.
```ts
UIStore.update([setDarkMode, setTypography("Roboto)]);
```
--------------------------------
### Example Usage of InjectStoreState
Source: https://github.com/lostpebble/pullstate/blob/master/docs/inject-store-state.md
An example demonstrating how to use the InjectStoreState component to display a user's name. It selects the 'userName' property from the UserStore and renders it within a span.
```tsx
const GreetUser = () => {
return (
s.userName}>
{userName => Hi, {userName}!}
)
}
```
--------------------------------
### Firebase Realtime Database Integration Example
Source: https://github.com/lostpebble/pullstate/blob/master/docs/subscribe.md
This example shows how to integrate with Firebase Realtime Database by subscribing to changes in a specific city's data based on a `currentCityCode` in the store. It manages the subscription lifecycle by unsubscribing from the previous city's data when the `currentCityCode` changes.
```APIDOC
## Firebase Realtime Database Integration Example
```tsx
let previousCityUnsubscribe = () => null;
function startWatchingCity(cityCode) {
return db.collection("cities")
.doc(cityCode)
.onSnapshot(function(doc) {
CityStore.update(s => {
s.watchedCities[cityCode] = { updated: new Date(), data: doc.data() };
});
});
}
function getRealtimeUpdatesForCityCode(cityCode) {
previousCityUnsubscribe();
previousCityUnsubscribe = startWatchingCity(cityCode);
}
CityStore.subscribe(s => s.currentCityCode, getRealtimeUpdatesForCityCode);
// inside some initialization code on the client (run after initial
// store hydration, if any), start watching initial city
function initializeClientThings() {
getRealtimeUpdatesForCityCode(CityStore.getRawState().currentCityCode);
}
```
```
--------------------------------
### Async Action Example (TypeScript)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
TypeScript version of the async action example. It includes input and output interfaces for type safety and uses `createAsyncAction` with generic types.
```tsx
import { createAsyncAction, errorResult, successResult } from "pullstate";
interface IOSearchPicturesForTagInput {
tag: string;
}
interface IOSearchPicturesForTagOutput {
pictures: Picture[];
}
const searchPicturesForTag = createAsyncAction(
async ({ tag }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
return successResult({ pictures: result.pictures });
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
}
);
export const PictureExample = (props: { tag: string }) => {
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
if (!finished) {
return
Loading Pictures for tag "{props.tag}"
;
}
if (result.error) {
return
{result.message}
;
}
return ;
};
```
--------------------------------
### Example Cache Break Hook
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-cache-break-hook.md
An example demonstrating how to implement a cache break hook to invalidate a search API result if it's older than 30 seconds.
```APIDOC
## Example: Invalidate cache if older than 30 seconds
### Description
This example shows a cache break hook that invalidates the cached result if it's not an error and the `timeCached` is more than 30 seconds old.
### Code
```typescript
const THIRTY_SECONDS = 30 * 1000;
// In your action creator:
cacheBreakHook: ({ result, timeCached }) =>
!result.error && timeCached + THIRTY_SECONDS < Date.now(),
```
### Explanation
The hook checks if the `result` does not contain an `error` and if the `timeCached` plus `THIRTY_SECONDS` is less than the current time (`Date.now()`). If both conditions are true, the cache is broken.
```
--------------------------------
### Example Reaction for Cron Tab
Source: https://github.com/lostpebble/pullstate/blob/master/docs/reactions.md
An example of a reaction that watches a 'crontab' string and updates the store with calculated human-readable times and dates. It handles null values and potential errors from the cron parsing utility.
```tsx
CronJobStore.createReaction(s => s.crontab, (crontab, draft) => {
if (crontab !== null) {
const resp = utils.getTimesAndTextFromCronTab({ crontab });
if (resp.positive) {
draft.currentCronJobTimesAndText = resp.payload;
} else {
draft.currentCronJobTimesAndText = {
currentCronJobTimesAndText: {
text: `Bad crontab`,
times: {
prevTime: null,
nextTime: null,
},
},
};
}
} else {
draft.currentCronJobTimesAndText = {
times: {
prevTime: null,
nextTime: null,
},
text: `No crontab`,
};
}
}
);
```
--------------------------------
### Handling API Data Fetching and Store Updates
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-introduction.md
This example demonstrates a common pattern for fetching data from an API and updating the store with the results or any errors encountered. It's particularly useful for server-rendering scenarios where data needs to be resolved before the client receives the page.
```jsx
try {
const posts = await PostApi.getPostListForTag(tag);
instance.stores.PostStore.update(s => {
s.posts = posts;
});
} catch (e) {
instance.stores.PostStore.update(s => {
s.posts = [];
s.postError = e.message;
});
}
```
--------------------------------
### Install Pullstate with Yarn
Source: https://github.com/lostpebble/pullstate/blob/master/docs/installation.md
Use this command to add Pullstate to your project dependencies via Yarn.
```powershell
yarn add pullstate
```
--------------------------------
### Using postActionHook to Update GalleryStore
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-post-action-hook.md
This example demonstrates the correct usage of postActionHook to update the GalleryStore. The state update logic is moved outside the main action, ensuring it runs regardless of cache hits.
```tsx
const searchPicturesForTag = PullstateCore.createAsyncAction(
async ({ tag }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
return successResult(result);
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
},
{
postActionHook: ({ result, stores }) => {
if (!result.error) {
stores.GalleryStore.update(s => {
s.pictures = result.payload.pictures;
});
}
},
}
);
```
--------------------------------
### Updating GalleryStore inside Async Action (Naive Approach)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-post-action-hook.md
This example shows a naive approach where state is updated directly within the async action. This can lead to stale state if the action's result is cached and not re-run.
```tsx
const searchPicturesForTag = PullstateCore.createAsyncAction(async ({ tag }, stores) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
stores.GalleryStore.update(s => {
s.pictures = result.pictures;
});
return successResult();
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
});
```
--------------------------------
### Leaflet tile change Example
Source: https://github.com/lostpebble/pullstate/blob/master/docs/subscribe.md
This example demonstrates how to use `subscribe` within a `useEffect` hook to update a Leaflet tile layer's source when a specific part of the store's state changes. The `unsubscribe` function is used in the cleanup phase of the `useEffect` hook.
```APIDOC
## Leaflet tile change Example
```tsx
// a useEffect() hook in a functional component
useEffect(() => {
const tileLayer = L.tileLayer(tileTemplate.url, {
minZoom: 3,
maxZoom: 18,
}).addTo(mapRef.current);
const unsubscribeFromTileTemplate = GISStore.subscribe(
s => s.tileLayerTemplate,
newTemplate => {
tileLayer.setUrl(newTemplate.url);
}
);
return () => {
unsubscribeFromTileTemplate();
};
}, []);
```
```
--------------------------------
### Use Hook to Initiate and Watch Async Action
Source: https://context7.com/lostpebble/pullstate/llms.txt
The `useBeckon` hook automatically starts an async action on component mount and subscribes to its state. It returns `[finished, result, updating]`. Use `holdPrevious: true` to maintain previous results during updates and `ssr: true` for server-side rendering.
```tsx
import { searchPicturesForTag } from "./actions/searchPicturesForTag";
export const PictureGallery = ({ tag }: { tag: string }) => {
const [finished, result, updating] = searchPicturesForTag.useBeckon(
{ tag },
{
holdPrevious: true, // keep showing old results while new ones load
ssr: true, // resolve on server (default); pass false for client-only
}
);
if (!finished) return
Loading pictures for "{tag}"...
;
if (result.error) return
Error: {result.message}
;
return (
);
};
```
--------------------------------
### Using Async Action Hook in Component
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
Demonstrates how to use the `useBeckon` hook to trigger an async action and manage its loading, error, and success states within a React component. This example is shared between client-side and server-rendered contexts.
```typescript
export const PictureExample = (props: { tag: string }) => {
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
if (!finished) {
return
Loading Pictures for tag "{props.tag}"
;
}
if (result.error) {
return
{result.message}
;
}
// Inside the Gallery component we will pull our state
// from our stores directly instead of passing it as a prop
return ;
};
```
--------------------------------
### Beckon an Async Action (React hook)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Use `useBeckon` to initiate an Async Action upon the hook's first call. It provides `finished`, `result`, and `updating` states. The `started` state is omitted as the action begins immediately.
```tsx
const [finished, result, updating] = searchPicturesForTag.useBeckon({ tag }, options);
```
--------------------------------
### Access stores directly (client-only)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/quick-example-server-rendered.md
For client-only applications, stores can be imported and used directly without needing createPullstateCore. This example shows how to use useStores imported from 'pullstate'.
```tsx
import { useStores } from "pullstate";
// in app component
const { UIStore } = useStores();
const isDarkMode = UIStore.useState(s => s.isDarkMode);
```
--------------------------------
### Client-Side State Hydration with Pullstate
Source: https://github.com/lostpebble/pullstate/blob/master/docs/quick-example-server-rendered.md
Instantiate PullstateCore on the client with ssr: false and the hydrateSnapshot obtained from the server. This ensures the client application starts with the server-rendered state.
```tsx
const hydrateSnapshot = JSON.parse(window.__PULLSTATE__);
const instance = PullstateCore.instantiate({ ssr: false, hydrateSnapshot });
ReactDOM.render(
,
document.getElementById("react-mount")
);
```
--------------------------------
### Realtime Database Listener for City Data
Source: https://github.com/lostpebble/pullstate/blob/master/docs/subscribe.md
This example demonstrates setting up a Firebase realtime database listener that updates the CityStore whenever data for a specific city changes. It includes logic to unsubscribe from the previous listener when the watched city code changes.
```tsx
let previousCityUnsubscribe = () => null;
function startWatchingCity(cityCode) {
return db.collection("cities")
.doc(cityCode)
.onSnapshot(function(doc) {
CityStore.update(s => {
s.watchedCities[cityCode] = { updated: new Date(), data: doc.data() };
});
});
}
function getRealtimeUpdatesForCityCode(cityCode) {
previousCityUnsubscribe();
previousCityUnsubscribe = startWatchingCity(cityCode);
}
CityStore.subscribe(s => s.currentCityCode, getRealtimeUpdatesForCityCode);
```
```tsx
function initializeClientThings() {
getRealtimeUpdatesForCityCode(CityStore.getRawState().currentCityCode);
}
```
```tsx
CityStore.update(s => {
s.currentCityCode = newCode;
})
```
--------------------------------
### Watch an Async Action (React hook)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
The `useWatch` hook allows you to listen to the states of an Async Action without initiating it. It returns the action's states: `started`, `finished`, `result`, and `updating`.
```APIDOC
## Watch an Async Action (React hook)
```ts
const [started, finished, result, updating] = searchPicturesForTag.useWatch({ tag }, options);
```
### Description
This React hook "watches" the action, meaning it listens for when the action starts and tracks all subsequent states.
### States
* `started`: The action has begun execution.
* `finished`: The action has completed.
* `updating`: Indicates an ongoing update, potentially with `holdPrevious: true`.
### Parameters
* `args` (object): Arguments for the async action.
* `options` (object): Optional configuration for the watch behavior.
* `postActionEnabled` (boolean): Whether post-action hooks are enabled.
* `cacheBreakEnabled` (boolean): Whether cache breaking is enabled.
* `holdPrevious` (boolean): Whether to hold the previous result during updates.
* `dormant` (boolean): If true, the action will not execute or listen.
```
--------------------------------
### Create and Log Pullstate Store
Source: https://github.com/lostpebble/pullstate/blob/master/test/test.umd.html
Demonstrates creating a new Pullstate store with initial state and logging its contents. Also shows how to subscribe to a specific part of the store's state.
```javascript
const TestStore = new pullstate.Store({ hi: "hello", }); console.log(TestStore); TestStore.subscribe(s => s.hi, (text) => console.log(text));
```
--------------------------------
### Async Action Options for run()
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Optional configuration for the `run()` method, allowing control over whether the action is treated as an update, respects cache, or ignores short-circuiting.
```tsx
const result = await searchPicturesForTag.run({ tag }, options);
```
```tsx
interface Options {
treatAsUpdate: boolean, // default = false
respectCache: boolean, // default = false
ignoreShortCircuit: boolean, // default = false
}
```
--------------------------------
### Async Action Configuration Options
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-other-options.md
Illustrates the optional configuration object that can be passed to createAsyncAction. These include subsetKey for cache fingerprinting and forceContext for SSR compatibility.
```json
{
subsetKey: (args: any) => string;
forceContext: boolean;
}
```
--------------------------------
### Example: Invalidate Cache After 30 Seconds
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-cache-break-hook.md
This example demonstrates how to use the cache break hook to invalidate a cached search API result if it's older than 30 seconds. `timeCached` represents the epoch time when the action last completed.
```javascript
const THIRTY_SECONDS = 30 * 1000;
// The cache break hook in your action creator
cacheBreakHook: ({ result, timeCached }) =>
!result.error && timeCached + THIRTY_SECONDS < Date.now(),
```
--------------------------------
### Create Async Action with Options
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-other-options.md
Demonstrates the basic structure for creating an async action with hooks and other options. Ensure 'createAsyncAction' is imported from 'pullstate'.
```tsx
import { createAsyncAction } from "pullstate";
const myAsyncAction = createAsyncAction(action, hooksAndOptions);
```
--------------------------------
### Create Async Action (Server-Rendered)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
For server-rendered apps, use `PullstateCore.createAsyncAction` to enable server-side pre-fetching. This requires importing `PullstateCore`.
```tsx
import { PullstateCore } from "./PullstateCore";
const myAsyncAction = PullstateCore.createAsyncAction(action, hooksAndOptions);
```
--------------------------------
### Add New Blog Post
Source: https://github.com/lostpebble/pullstate/blob/master/website/README.md
Create a new blog post by naming the markdown file with a date prefix in `website/blog/` and adding a header link in `website/siteConfig.js`.
```md
---
author: Frank Li
authorURL: https://twitter.com/foobarbaz
authorFBID: 503283835
title: New Blog Post
---
Lorem Ipsum...
```
--------------------------------
### Set up Pullstate Core for Server-Rendering (TypeScript)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/_removed.md
Initialize the Pullstate core with all your stores for server-rendering. This ensures that your application's state is correctly hydrated on the server.
```typescript
// PullstateCore.ts
import { UIStore } from "./stores/UIStore";
import { createPullstateCore } from "pullstate";
export const PullstateCore = createPullstateCore({
UIStore
});
```
--------------------------------
### AsyncAction.useWatch
Source: https://context7.com/lostpebble/pullstate/llms.txt
Similar to `useBeckon`, this hook watches an async action's state without initiating it. It returns `[started, finished, result, updating]`.
```APIDOC
## `AsyncAction.useWatch(args, options?)` — Hook that watches without initiating
Like `useBeckon`, but does NOT start the action. Only subscribes to state changes triggered externally (e.g., via `run()`). Returns `[started, finished, result, updating]`.
```tsx
import { searchPicturesForTag } from "./actions/searchPicturesForTag";
const PictureStatus = ({ tag }: { tag: string }) => {
const [started, finished, result, updating] = searchPicturesForTag.useWatch(
{ tag },
{ holdPrevious: true }
);
if (!started) return
;
return ;
};
// Trigger from somewhere else in the app:
const handleSearch = async (tag: string) => {
await searchPicturesForTag.run({ tag }, { treatAsUpdate: true });
};
```
```
--------------------------------
### Create Reaction - JavaScript
Source: https://github.com/lostpebble/pullstate/blob/master/docs/reactions.md
Register a reaction on a store using JavaScript. The `watch` function selects the value to monitor, and the `reaction` function defines the state update logic.
```jsx
StoreName.createReaction(watch, reaction);
```
--------------------------------
### PullstateCore.useStores()
Source: https://context7.com/lostpebble/pullstate/llms.txt
A React hook (on the core object) that returns the request-scoped store instances from `` context. Required when server-rendering so each request gets its own isolated store copies.
```APIDOC
## `PullstateCore.useStores()` — Access SSR stores inside components
A React hook (on the core object) that returns the request-scoped store instances from `` context. Required when server-rendering so each request gets its own isolated store copies.
```tsx
import { PullstateCore } from "./PullstateCore";
const App = () => {
const { UIStore, UserStore } = PullstateCore.useStores();
const isDarkMode = UIStore.useState(s => s.isDarkMode);
const userName = UserStore.useState(s => s.profile.name);
return (
Welcome, {userName}
);
};
```
```
--------------------------------
### Creating an Async Action with Hooks
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-hooks-overview.md
Demonstrates how to pass hooks when creating an async action. The second argument to `createAsyncAction` is an object where hook functions can be defined.
```tsx
const searchPicturesForTag = createAsyncAction(async ({ tag }) => {
// action code
}, hooksGoHere);
```
--------------------------------
### InjectStoreState Component (JavaScript)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/inject-store-state.md
The JavaScript implementation of the InjectStoreState component. It uses the useStoreState hook to get the state from the provided store and passes it to the children render prop.
```jsx
function InjectStoreState({ store, on, children }) {
const state = useStoreState(store, on);
return children(state);
}
```
--------------------------------
### Update store state with interaction
Source: https://github.com/lostpebble/pullstate/blob/master/docs/quick-example-server-rendered.md
Add interaction to a React component by updating the store's state using the update method. This example toggles the 'isDarkMode' state.
```tsx
const { UIStore } = PullstateCore.useStores();
const isDarkMode = UIStore.useState(s => s.isDarkMode);
return (
Hello Pullstate
);
```
--------------------------------
### InjectAsyncAction Component Example
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Demonstrates how to use the InjectAsyncAction component to display an image that fades in once completely loaded. This component injects the async state directly into the React app without needing a hook.
```tsx
async function loadImageFully(src: string) {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = resolve;
img.onerror = reject;
img.src = src;
});
}
export const AsyncActionImageLoad = createAsyncAction<{ src: string }>(async ({ src }) => {
await loadImageFully(src);
return successResult();
});
```
```tsx
{([finished]) => {
return
}}
```
--------------------------------
### Async Action Options for useWatch
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Configuration options for `useWatch` that control behavior like enabling post-action hooks, cache breaks, holding previous results, or making the action dormant.
```ts
{
postActionEnabled?: boolean;
cacheBreakEnabled?: boolean;
holdPrevious?: boolean;
dormant?: boolean;
}
```
--------------------------------
### Short Circuit Hook Example
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-short-circuit-hook.md
Use this hook to conditionally prevent an async action from running. Return `successResult` with an empty list to avoid a search query for terms less than 2 characters.
```tsx
shortCircuitHook: ({ args }) => {
if (args.text.length <= 1) {
return successResult({ posts: [] });
}
return false;
},
```
--------------------------------
### Optimized State Selection with Paths
Source: https://github.com/lostpebble/pullstate/blob/master/Changelog.md
Utilize `useStoreStateOpt` with an array of path selections for optimized state updates. This method leverages immer patches to re-render components only when specific paths in the state change, improving performance by avoiding broad equality checks.
```typescript
const [isDarkMode] = useStoreStateOpt(UIStore, [["isDarkMode"]]);
```
--------------------------------
### Accessing Pullstate Instance with useInstance
Source: https://github.com/lostpebble/pullstate/blob/master/Changelog.md
Use the `useInstance` convenience method or `PullstateCore.useInstance` to get direct access to your Pullstate instance within components. This is particularly helpful for improved typing and more direct interaction with your store instances.
```typescript
const uiStoreInstance = PullstateCore.useInstance(UIStore);
```
--------------------------------
### Using useStoreState with Dynamic Selections and Dependencies
Source: https://github.com/lostpebble/pullstate/blob/master/docs/use-store-state-hook.md
Explains how to use the third argument, a dependency array, with useStoreState when your getSubState selector function relies on dynamic values. This ensures the selection is re-evaluated when dependencies change.
```APIDOC
## Dynamic Sub-State Selection with Dependencies
### Description
Enables dynamic selection of sub-state when the `getSubState` function depends on external values. A dependency array ensures the selection is re-evaluated when these values change.
### Usage
```tsx
const MyComponent = ({ type }) => {
const data = useStoreState(MyStore, (s) => s[type], [type]);
// OR
const data = MyStore.useState((s) => s[type], [type]);
// do stuff with data
}
```
### Parameters
* `store` (Store): The Pullstate store to access.
* `getSubState` (function): A function that takes the store's state and returns the desired sub-state, potentially using dynamic values.
* `dependencies` (Array): An array of values that, when changed, will cause the `getSubState` selection to be re-evaluated. Similar to `useEffect` dependencies.
```
--------------------------------
### instance.runAsyncAction(action, args, options?)
Source: https://context7.com/lostpebble/pullstate/llms.txt
Runs an async action directly on a Pullstate SSR instance before rendering, pre-caching the result so `useBeckon()` finds it immediately without triggering re-renders.
```APIDOC
## `instance.runAsyncAction(action, args, options?)` — Pre-fetch async actions on server
Runs an async action directly on a Pullstate SSR instance before rendering, pre-caching the result so `useBeckon()` finds it immediately without triggering re-renders.
```tsx
import { PullstateCore } from "./PullstateCore";
import { CronJobActions } from "./actions/CronJobActions";
// Koa + koa-router SSR pre-fetch pattern
ServerRouter.get("/*", async (ctx, next) => {
ctx.state.pullstate = PullstateCore.instantiate({ ssr: true });
await next();
});
ServerRouter.get("/cron-jobs", async (ctx, next) => {
await ctx.state.pullstate.runAsyncAction(CronJobActions.getList, { limit: 30 });
await next();
});
ServerRouter.get("/cron-job/:id", async (ctx, next) => {
await ctx.state.pullstate.runAsyncAction(
CronJobActions.getDetail,
{ id: ctx.params.id },
{ respectCache: true } // use on client re-render to avoid double-fetching
);
await next();
});
ServerRouter.get("*", async (ctx) => {
const html = ReactDOMServer.renderToString(
);
ctx.body = html;
});
```
```
--------------------------------
### Get, Set, and Update Async Action Cache
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Provides methods to interact with the asynchronous action cache. `getCached` retrieves cached data, `setCached` updates it with new results, and `updateCached` modifies it using an updater function.
```tsx
searchPicturesForTag.getCached(args, options);
```
```tsx
searchPicturesForTag.setCached(args, result, options);
```
```tsx
searchPicturesForTag.updateCached(args, updater, options);
```
--------------------------------
### Async Action Options for read()
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Options for the `read` method, which can be used to control the enabling of post-action and cache-breaking hooks during data fetching.
```ts
interface Options {
postActionEnabled?: boolean;
cacheBreakEnabled?: boolean;
}
```
--------------------------------
### createPullstateCore(stores)
Source: https://context7.com/lostpebble/pullstate/llms.txt
Groups multiple stores under a single "core" for server-side rendering. The core is used to instantiate per-request store copies, provide them via ``, and create Async Actions that have access to those request-scoped stores.
```APIDOC
## `createPullstateCore(stores)` — SSR store collection
Groups multiple stores under a single "core" for server-side rendering. The core is used to instantiate per-request store copies, provide them via ``, and create Async Actions that have access to those request-scoped stores.
```tsx
// PullstateCore.ts
import { createPullstateCore } from "pullstate";
import { UIStore } from "./stores/UIStore";
import { UserStore } from "./stores/UserStore";
import { GalleryStore } from "./stores/GalleryStore";
export const PullstateCore = createPullstateCore({ UIStore, UserStore, GalleryStore });
// In a server request handler (e.g., Express / Koa)
import ReactDOMServer from "react-dom/server";
import { PullstateProvider } from "pullstate";
import { PullstateCore } from "./PullstateCore";
import { App } from "./App";
async function handleRequest(req, res) {
const instance = PullstateCore.instantiate({ ssr: true });
// Pre-load state from request data
const prefs = await UserApi.getUserPreferences(req.user.id);
instance.stores.UIStore.update(s => { s.isDarkMode = prefs.isDarkMode; });
// Re-render loop until all useBeckon() async actions are resolved
let html = ReactDOMServer.renderToString(
);
while (instance.hasAsyncStateToResolve()) {
await instance.resolveAsyncState();
html = ReactDOMServer.renderToString(
);
}
const snapshot = JSON.stringify(instance.getPullstateSnapshot())
.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
res.send(`${html}`);
}
```
```
--------------------------------
### Accessing Entire Store State
Source: https://github.com/lostpebble/pullstate/blob/master/docs/use-store-state-hook.md
Use this method to get the complete state of a store. It's generally not recommended as it can lead to unnecessary re-renders if only a small part of the state is needed. The component re-renders whenever any value in the store changes.
```tsx
const allUIState = useStoreState(UIStore);
return (allUIState.isDarkMode ? : );
```
```tsx
const allUIState = UIStore.useState();
```
--------------------------------
### Watch an Async Action (React hook)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Use `useWatch` to listen to an Async Action's states without initiating it. It tracks `started`, `finished`, and `updating` states, and provides the `result` object. This hook is useful for observing actions triggered elsewhere.
```ts
const [started, finished, result, updating] = searchPicturesForTag.useWatch({ tag }, options);
```
--------------------------------
### new Store(initialState)
Source: https://context7.com/lostpebble/pullstate/llms.txt
Defines and exports a reactive state container initialized with a plain object. TypeScript users can pass an interface generic for full type safety throughout the app.
```APIDOC
## new Store(initialState) - Create a state store
Defines and exports a reactive state container initialized with a plain object. TypeScript users can pass an interface generic for full type safety throughout the app.
```tsx
import { Store } from "pullstate";
interface IUIStore {
isDarkMode: boolean;
fontSize: number;
currentUser: string | null;
}
export const UIStore = new Store({
isDarkMode: true,
fontSize: 16,
currentUser: null,
});
```
```
--------------------------------
### Holding Previous Results in Async Hooks
Source: https://github.com/lostpebble/pullstate/blob/master/Changelog.md
Enable `holdPrevious: true` in `useWatch()` or `useBeckon()` to retain the last successful result while a new action is in progress. The returned value will include `started`, `finished`, `result`, and `updating` flags to manage the UI state during asynchronous operations.
```typescript
const [started, finished, result, updating] = useWatch(myAction, { holdPrevious: true });
```
--------------------------------
### Use createReaction in a useEffect Hook
Source: https://github.com/lostpebble/pullstate/blob/master/docs/_removed.md
Subscribe to state changes within a React component using `createReaction`. This example demonstrates how to update a Leaflet tile layer when the `tileLayerTemplate` in the GISStore changes. Remember to unsubscribe from the reaction in the cleanup function of `useEffect` to prevent memory leaks.
```javascript
// a useEffect() hook in our functional component
useEffect(() => {
const tileLayer = L.tileLayer(tileTemplate.url, {
minZoom: 3,
maxZoom: 18,
}).addTo(mapRef.current);
const unsubscribeFromTileTemplate = GISStore.createReaction(
s => s.tileLayerTemplate,
newTemplate => {
tileLayer.setUrl(newTemplate.url);
}
);
return () => {
unsubscribeFromTileTemplate();
};
}, []);
```
--------------------------------
### Creating an Async Action for Client-Side Apps
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
Defines an asynchronous action that fetches pictures based on a tag, updates the `GalleryStore`, and returns a success or error result. This is suitable for client-side applications.
```typescript
import { createAsyncAction, errorResult, successResult } from "pullstate";
import { GalleryStore } from "./stores/GalleryStore";
const searchPicturesForTag = createAsyncAction(async ({ tag }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
GalleryStore.update(s => {
s.pictures = result.pictures;
});
return successResult();
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
});
export const PictureExample = (props: { tag: string }) => {
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
if (!finished) {
return
Loading Pictures for tag "{props.tag}"
;
}
if (result.error) {
return
{result.message}
;
}
// Inside the Gallery component we will pull our state
// from our stores directly instead of passing it as a prop
return ;
};
```
--------------------------------
### Async Action Options for useBeckon
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
Options for `useBeckon`, including those for disabling hooks, holding previous results, making the action dormant, and controlling server-side rendering behavior.
```tsx
{
postActionEnabled?: boolean;
cacheBreakEnabled?: boolean;
holdPrevious?: boolean;
dormant?: boolean;
ssr?: boolean;
}
```
--------------------------------
### Run an Async Action Directly
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
The `run` method executes an Async Action directly, similar to a standard JavaScript promise. It's useful for initiating watched actions or triggering updates.
```APIDOC
## Run an Async Action directly
```tsx
const result = await searchPicturesForTag.run({ tag });
```
### Description
Executes the async action directly, behaving like a standard promise. This is often used to trigger watched actions or initiate updates.
### Parameters
* `args` (object): Arguments for the async action.
* `options` (object): Optional configuration for the run behavior.
* `treatAsUpdate` (boolean): Default = `false`. Treat this run as an update.
* `respectCache` (boolean): Default = `false`. Whether to respect caching mechanisms.
* `ignoreShortCircuit` (boolean): Default = `false`. Whether to ignore short-circuiting logic.
```
--------------------------------
### PullstateCore.instantiate()
Source: https://context7.com/lostpebble/pullstate/llms.txt
Hydrates the client-side stores with the state snapshot serialized from the server, ensuring React's hydration matches the server-rendered HTML exactly.
```APIDOC
## Client-side SSR hydration with `PullstateCore.instantiate`
Hydrates the client-side stores with the state snapshot serialized from the server, ensuring React's hydration matches the server-rendered HTML exactly.
```tsx
// client entry point (e.g., index.tsx)
import ReactDOM from "react-dom";
import { PullstateProvider } from "pullstate";
import { PullstateCore } from "./PullstateCore";
import { App } from "./App";
const hydrateSnapshot = JSON.parse(window.__PULLSTATE__);
const instance = PullstateCore.instantiate({ ssr: false, hydrateSnapshot });
ReactDOM.hydrate(
,
document.getElementById("react-mount")
);
```
```
--------------------------------
### Create a new Store with TypeScript
Source: https://context7.com/lostpebble/pullstate/llms.txt
Define a reactive state container using `new Store()` and provide an interface for type safety.
```tsx
import { Store } from "pullstate";
interface IUIStore {
isDarkMode: boolean;
fontSize: number;
currentUser: string | null;
}
export const UIStore = new Store({
isDarkMode: true,
fontSize: 16,
currentUser: null,
});
```
--------------------------------
### Run Async Actions for List Route
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-server-rendering.md
Execute the `getCronJobs` async action with a specified limit before proceeding to the next middleware. This pre-caches the data for the list page.
```tsx
ServerReactRouter.get("/list-cron-jobs", async (ctx, next) => {
await ctx.state.pullstateInstance.runAsyncAction(CronJobAsyncActions.getCronJobs, { limit: 30 });
await next();
});
```
--------------------------------
### Using useStoreState to Access Sub-State
Source: https://github.com/lostpebble/pullstate/blob/master/docs/use-store-state-hook.md
The recommended way to use useStoreState, this method allows you to select and access a specific portion of your store's state, optimizing re-renders to only occur when the selected sub-state changes.
```APIDOC
## Accessing Sub-State
### Description
Selects and retrieves a specific sub-state from a Pullstate store. This is the recommended approach for performance.
### Usage
```tsx
const isDarkMode = useStoreState(UIStore, s => s.isDarkMode);
return (isDarkMode ? : );
```
### Parameters
* `store` (Store): The Pullstate store to access.
* `getSubState` (function): A function that takes the store's state (`s`) and returns the desired sub-state.
### Return Value
The selected sub-state.
### Notes
* Components will only re-render if the selected sub-state's value changes.
* Equivalent to `UIStore.useState(s => s.isDarkMode)`.
```
--------------------------------
### Configure Sidebar with New Doc
Source: https://github.com/lostpebble/pullstate/blob/master/website/README.md
Add the ID of a newly created documentation page to an existing category within the `website/sidebar.json` file.
```javascript
// Add newly-created-doc to the Getting Started category of docs
{
"docs": {
"Getting Started": [
"quick-start",
"newly-created-doc" // new doc here
],
...
},
...
}
```
--------------------------------
### Create and Use an Async Action with useBeckon
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-server-rendering.md
Defines an asynchronous action for fetching pictures based on a tag and uses the `useBeckon` hook in a React component to manage its state during rendering. Ensure data-fetching functions are isomorphic.
```tsx
const searchPicturesForTag = PullstateCore.createAsyncAction(async ({ tag }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
return successResult(result.pictures);
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
});
export const PictureExample = (props: { tag: string }) => {
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
if (!finished) {
return
Loading Pictures for tag "{props.tag}"
;
}
if (result.error) {
return
{result.message}
;
}
return ;
};
```
```tsx
const [finished, result] = searchPicturesForTag.useBeckon({ tag: props.tag });
```
--------------------------------
### Beckon an Async Action (React hook)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-action-use.md
The `useBeckon` hook initiates an Async Action when the hook is first called. It returns the action's states: `finished`, `result`, and `updating`.
```APIDOC
## Beckon an Async Action (React hook)
```tsx
const [finished, result, updating] = searchPicturesForTag.useBeckon({ tag }, options);
```
### Description
This hook is similar to `useWatch`, but it automatically instigates the Async Action upon the initial call.
### States
* `finished`: The action has completed.
* `result`: The structured result object returned from the action.
* `updating`: Indicates an ongoing update.
### Parameters
* `args` (object): Arguments for the async action.
* `options` (object): Optional configuration for the beckon behavior.
* `postActionEnabled` (boolean): Whether post-action hooks are enabled.
* `cacheBreakEnabled` (boolean): Whether cache breaking is enabled.
* `holdPrevious` (boolean): Whether to hold the previous result during updates.
* `dormant` (boolean): If true, the action will not execute or listen.
* `ssr` (boolean): If false, disables action initiation during server-rendering.
```
--------------------------------
### Add New Docs Page to Sidebar
Source: https://github.com/lostpebble/pullstate/blob/master/website/README.md
Create a new documentation page in `docs/` and reference its ID in `website/sidebar.json` to include it in a sidebar category.
```md
---
id: newly-created-doc
title: This Doc Needs To Be Edited
---
My new content here..
```
--------------------------------
### Creating an Async Action for Server-Rendered Apps
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-actions-creating.md
Defines an asynchronous action for server-rendered applications, utilizing `PullstateCore.createAsyncAction` and accessing stores via the second argument. This ensures correct store updates within the server rendering context.
```typescript
import { PullstateCore } from "./PullstateCore";
const searchPicturesForTag = PullstateCore.createAsyncAction(
async ({ tag }, { GalleryStore }) => {
const result = await PictureApi.searchWithTag(tag);
if (result.success) {
GalleryStore.update(s => {
s.pictures = result.pictures;
});
return successResult();
}
return errorResult([], `Couldn't get pictures: ${result.errorMessage}`);
}
);
```
--------------------------------
### update() with Updater Function
Source: https://github.com/lostpebble/pullstate/blob/master/docs/update-store.md
Update your store's state by calling `update()` directly on your store with an updater function. The updater function receives the current state and allows direct mutation to create the next state, leveraging Immer's capabilities.
```APIDOC
## update()
### Description
Updates the store's state using an updater function.
### Method
`update(updater | updater[])`
### Parameters
#### Updater Function
- **updater** (function) - Required - A function that takes the current state and returns the next state.
### Request Example
```tsx
MyStore.update(s => {
s.someValue = 'newValue';
});
```
### Example with Multiple Updaters
```ts
MyStore.update([
s => { s.value1 = 'a'; },
s => { s.value2 = 'b'; }
]);
```
```
--------------------------------
### Create a UI Store with Pullstate (TypeScript)
Source: https://github.com/lostpebble/pullstate/blob/master/docs/_removed.md
Define a UI store to manage application-wide UI states like dark mode. This store is a fundamental building block for managing state with Pullstate.
```typescript
// UIStore.ts
import { Store } from "pullstate";
interface IUIStore {
isDarkMode: boolean;
}
export const UIStore = new Store({
isDarkMode: true,
});
```
--------------------------------
### AsyncAction.run
Source: https://context7.com/lostpebble/pullstate/llms.txt
Allows imperative execution of an async action, bypassing or respecting the cache. Useful for actions triggered by user interactions like form submissions or button clicks. Returns the action's result.
```APIDOC
## `AsyncAction.run(args, options?)` — Run an async action imperatively
Executes the action as a direct promise, bypassing or respecting cache. Useful for form submissions, button clicks, or pre-fetching. Returns the action result.
```tsx
import { searchPicturesForTag } from "./actions/searchPicturesForTag";
const SearchButton = ({ tag }: { tag: string }) => {
const handleClick = async () => {
const result = await searchPicturesForTag.run(
{ tag },
{
treatAsUpdate: true, // sets `updating=true` in watched hooks
respectCache: false, // always re-run (ignore cache)
ignoreShortCircuit: false,
}
);
if (result.error) {
console.error("Search failed:", result.message, result.tags);
}
};
return ;
};
```
```
--------------------------------
### Listening to Store Change Patches
Source: https://github.com/lostpebble/pullstate/blob/master/Changelog.md
Implement a `patchListener` with `Store.listenToPatches()` to receive notifications about changes to an entire store. This is useful for debugging or implementing custom logic that needs to react to granular state modifications.
```typescript
UIStore.listenToPatches((patches, inversePatches) => {
console.log('Patches applied:', patches);
});
```
--------------------------------
### Server-Side Rendering with Re-render Until Resolved
Source: https://github.com/lostpebble/pullstate/blob/master/docs/async-server-rendering.md
This code demonstrates how to resolve asynchronous state on the server by repeatedly rendering the React application until all async actions are completed. It initializes a PullstateCore instance with `ssr: true`, renders the app, and then enters a loop to resolve async state and re-render until no more state needs resolution. Finally, it captures the snapshot for client-side hydration.
```tsx
const instance = PullstateCore.instantiate({ ssr: true });
const app = (
)
let reactHtml = ReactDOMServer.renderToString(app);
while (instance.hasAsyncStateToResolve()) {
await instance.resolveAsyncState();
reactHtml = ReactDOMServer.renderToString(app);
}
const snapshot = instance.getPullstateSnapshot();
const body = `
${reactHtml}`;
```
--------------------------------
### Create Reaction - TypeScript
Source: https://github.com/lostpebble/pullstate/blob/master/docs/reactions.md
Register a reaction on a store using TypeScript, including type definitions for the reaction function. The `watch` function selects the value to monitor, and the `reaction` function defines the state update logic.
```tsx
type TReactionFunction = (watched: T, draft: S, original: S, lastWatched: T) => void;
StoreName.createReaction(watch: (state: S) => T, reaction: TReactionFunction): () => void
```