### 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
Search not yet initiated
; if (!finished) return
Searching...
; if (result.error) return
Error: {result.message} (tags: {result.tags.join(", ")})
; 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 ```