### Install Remesh Logger Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Install the logger package for debugging. ```sh # via npm npm install --save remesh-logger # via yarn yarn add remesh-logger ``` -------------------------------- ### Install Remesh React Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Install the necessary package for React integration. ```sh # via npm npm install --save remesh-react # via yarn yarn add remesh-react ``` -------------------------------- ### Install Remesh and RxJS Source: https://github.com/remesh-js/remesh/blob/main/README_zh_CN.md Commands to install the required dependencies for Remesh projects. ```bash # Install remesh and rxjs via npm npm install --save remesh rxjs # Install remesh and rxjs via yarn yarn add remesh rxjs ``` -------------------------------- ### Bootstrap the blog-starter-typescript project Source: https://github.com/remesh-js/remesh/blob/main/projects/nextjs-demo/README.md Use create-next-app to initialize the project with the specified example template. ```bash npx create-next-app --example blog-starter-typescript blog-starter-typescript-app # or yarn create next-app --example blog-starter-typescript blog-starter-typescript-app # or pnpm create next-app -- --example blog-starter-typescript blog-starter-typescript-app ``` -------------------------------- ### Install Remesh packages Source: https://context7.com/remesh-js/remesh/llms.txt Commands to install the core Remesh library, framework-specific bindings, and optional utilities. ```bash # Install remesh and rxjs via npm npm install --save remesh rxjs # For React applications npm install --save remesh-react # For Vue applications npm install --save remesh-vue # Optional packages npm install --save remesh-logger npm install --save remesh-redux-devtools npm install --save remesh-yjs ``` -------------------------------- ### Install Remesh and RxJS Source: https://github.com/remesh-js/remesh/blob/main/README.md Install the Remesh framework and RxJS library using either npm or yarn package managers. ```sh npm install --save remesh rxjs ``` ```sh yarn add remesh rxjs ``` -------------------------------- ### Install Remesh Redux DevTools Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Command to install the Redux DevTools integration for Remesh. ```bash yarn add remesh-redux-devtools ``` -------------------------------- ### Vue App Setup with RemeshVue Source: https://context7.com/remesh-js/remesh/llms.txt Configure the Vue application to use Remesh by creating a Remesh store and providing it to the Vue app instance using the RemeshVue plugin. This setup is essential for using Remesh composables in Vue components. ```typescript // main.ts - Vue app setup import { createApp } from 'vue' import { Remesh } from 'remesh' import { RemeshVue } from 'remesh-vue' import App from './App.vue' const store = Remesh.store({ name: 'VueApp' }) createApp(App).use(RemeshVue(store)).mount('#app') ``` -------------------------------- ### Install Redux DevTools Integration Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Install the package to connect Remesh with Redux DevTools. ```sh # via npm npm install --save remesh-redux-devtools ``` -------------------------------- ### React Integration with remesh-react Hooks Source: https://context7.com/remesh-js/remesh/llms.txt Shows how to use remesh-react hooks like useRemeshDomain, useRemeshQuery, useRemeshSend, and useRemeshEvent within React components to manage state and interact with Remesh domains. Includes examples of setting up RemeshRoot and RemeshScope. ```tsx import React from 'react' import ReactDOM from 'react-dom/client' import { Remesh } from 'remesh' import { RemeshRoot, RemeshScope, useRemeshDomain, useRemeshQuery, useRemeshSend, useRemeshEvent, useRemeshStore, } from 'remesh-react' // Define domain const TodoDomain = Remesh.domain({ name: 'TodoDomain', impl: (domain) => { const TodosState = domain.state({ name: 'TodosState', default: [] as string[] }) const TodosQuery = domain.query({ name: 'TodosQuery', impl: ({ get }) => get(TodosState()), }) const AddTodoCommand = domain.command({ name: 'AddTodoCommand', impl: ({ get }, title: string) => TodosState().new([...get(TodosState()), title]), }) const TodoAddedEvent = domain.event({ name: 'TodoAddedEvent' }) return { query: { TodosQuery }, command: { AddTodoCommand }, event: { TodoAddedEvent }, } }, }) // React component const TodoList = () => { const send = useRemeshSend() const todoDomain = useRemeshDomain(TodoDomain()) const todos = useRemeshQuery(todoDomain.query.TodosQuery()) const [input, setInput] = React.useState('') // Listen to domain events useRemeshEvent(todoDomain.event.TodoAddedEvent, (title) => { console.log('Todo added:', title) }) const handleAdd = () => { if (input.trim()) { send(todoDomain.command.AddTodoCommand(input)) setInput('') } } return (
setInput(e.target.value)} />
) } // With custom store const store = Remesh.store({ name: 'TodoApp' }) // App with RemeshRoot const App = () => ( ) // Or with options instead of store const AppWithOptions = () => ( ) // Use RemeshScope to keep domain alive const AppWithScope = () => ( ) ReactDOM.createRoot(document.getElementById('root')!).render() ``` -------------------------------- ### Vue Component with Remesh Domain Source: https://context7.com/remesh-js/remesh/llms.txt Demonstrates using Remesh domains within a Vue component with composables for state management, queries, commands, and events. Ensure remesh-vue is installed and configured. ```vue ``` -------------------------------- ### Create and Use Remesh Store Directly Source: https://context7.com/remesh-js/remesh/llms.txt Demonstrates the creation of a Remesh store, definition of a domain with state, queries, commands, and events, and how to interact with the store using methods like getDomain, igniteDomain, query, subscribeQuery, subscribeEvent, send, discardDomain, and discard. ```typescript import { Remesh } from 'remesh' // Create a store const store = Remesh.store({ name: 'MyApp', }) // Define a domain const CounterDomain = Remesh.domain({ name: 'CounterDomain', impl: (domain) => { const CountState = domain.state({ name: 'CountState', default: 0 }) const CountQuery = domain.query({ name: 'CountQuery', impl: ({ get }) => get(CountState()), }) const IncrementCommand = domain.command({ name: 'IncrementCommand', impl: ({ get }) => CountState().new(get(CountState()) + 1), }) const CountChangedEvent = domain.event({ name: 'CountChangedEvent' }) return { query: { CountQuery }, command: { IncrementCommand }, event: { CountChangedEvent }, } }, }) // Get domain from store const counterDomain = store.getDomain(CounterDomain()) // Activate domain effects store.igniteDomain(CounterDomain()) // Query current value const count = store.query(counterDomain.query.CountQuery()) console.log('Current count:', count) // Subscribe to query changes const querySubscription = store.subscribeQuery(counterDomain.query.CountQuery(), (newCount) => { console.log('Count changed to:', newCount) }) // Subscribe to events const eventSubscription = store.subscribeEvent(counterDomain.event.CountChangedEvent, (count) => { console.log('Count changed event:', count) }) // Send commands store.send(counterDomain.command.IncrementCommand()) // Send multiple commands at once store.send([ counterDomain.command.IncrementCommand(), counterDomain.command.IncrementCommand(), ]) // Cleanup querySubscription.unsubscribe() eventSubscription.unsubscribe() store.discardDomain(CounterDomain()) store.discard() // Discard all resources ``` -------------------------------- ### Creating and Using a Remesh Store Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Initialize a store to manage domains, subscribe to state changes, and dispatch commands. ```typescript import { Remesh } from 'Remesh' import YourDomain from 'your-domain' /** * Create a remesh store. */ const store = Remesh.store() /** * get domain from store. */ const yourDomain = store.getDomain(YourDomain()) /** * ignite domain for activating domain-effect if needed */ store.igniteDomain(YourDomain()) /** * subscribe the domain event */ */ store.subscribeEvent(yourDomain.event.YourEvent, (event) => { console.log(event) } /** * subscribe the domain query */ store.subscribeQuery(yourDomain.query.YourQuery(), (queryResult) => { console.log(queryResult) } /** * send command to your domain */ store.send(yourDomain.command.YourCommand('Hello, world!')) /** * Discard target domain resources */ store.discardDomain(YourDomain()) /** * discard all resource */ store.discard() ``` -------------------------------- ### Hooking into Command Lifecycles Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Use .before and .after methods on a command to execute logic surrounding the command execution. ```typescript const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const ACommand = domain.command({ name: 'ACommand', impl: ({ get }, arg: number) => { // ...do something }, }) ACommand.before(({ get }, arg) => { // do something *before* ACommand called return BeforeACommand(arg) }) ACommand.after(({ get }, arg) => { // do something *after* ACommand called return AfterACommand() }) }, }) ``` -------------------------------- ### Read State in a Command Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Access the current value of a state within a command using the get helper. ```typescript import { Remesh } from 'remesh' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const YourState = domain.state({ name: 'YourState', default: 0, }) const YourCommand = domain.command({ name: 'YourCommand', impl: ({ get }, ...args) => { const state = get(YourState()) }, }) }, }) ``` -------------------------------- ### Define Remesh Domain with States, Events, Commands, and Queries Source: https://github.com/remesh-js/remesh/blob/main/README_zh_CN.md This snippet demonstrates the complete definition of a Remesh domain, including its state, events, commands, and queries. It shows how to update state, emit events, and retrieve state values. ```typescript // domain.ts import { Remesh } from 'remesh' import { interval } from 'rxjs' import { map, switchMap, takeUntil } from 'rxjs/operators' type ChangeMode = 'increment' | 'decrement' /** * Define your domain model */ export const CountDomain = Remesh.domain({ name: 'CountDomain', impl: (domain) => { /** * Define your domain's related states */ const CountState = domain.state({ name: 'CountState', default: 0, }) /** * Define your domain's related events */ const CountChangedEvent = domain.event({ name: 'CountChangedEvent', }) /** * Define your domain's related commands */ const SetCountCommand = domain.command({ name: 'SetCountCommand', impl: ({}, count: number) => { /** * Update the domain's state and emit the related event */ return [CountState().new(count), CountChangedEvent(count)] }, }) /** * Define your domain's related queries */ const CountQuery = domain.query({ name: 'CountQuery', impl: ({ get }) => { /** * Get the domain's state */ return get(CountState()) }, }) /** * You can use a command in another command */ const IncreaseCountCommand = domain.command({ name: 'IncreaseCountCommand', impl: ({ get }, count: number = 1) => { return SetCountCommand(get(CountState()) + count) }, }) /** * You can use a command in another command */ const DecreaseCountCommand = domain.command({ name: 'DecreaseCountCommand', impl: ({ get }, count: number = 1) => { return SetCountCommand(get(CountState()) - count) }, }) const ChangeCountByModeCommand = domain.command({ name: 'ChangeCountByModeCommand', impl: ({}, mode: ChangeMode) => { if (mode === 'increment') return IncreaseCountCommand() if (mode === 'decrement') return DecreaseCountCommand() return null }, }) /** * Define an event for starting increment or decrement periodically */ const StartEvent = domain.event({ name: 'StartEvent', }) /** * Define a command to send event since event can't be sended outside of domain */ const StartCommand = domain.command({ name: 'StartCommand', impl: ({}, mode: ChangeMode) => { return StartEvent(mode) }, }) /** * Define an event for stopping signal */ const StopEvent = domain.event({ name: 'StopEvent', }) /** * Define a command to send event since event can't be sended outside of domain */ const StopCommand = domain.command({ name: 'StopCommand', impl: () => { return StopEvent() }, }) /** * Define your domain's related effects */ domain.effect({ name: 'ChangeCountEffect', impl: ({ fromEvent }) => { return fromEvent(StartEvent).pipe( switchMap((mode) => { return interval(100).pipe( map(() => ChangeCountByModeCommand(mode)), // finished when received stop event takeUntil(fromEvent(StopEvent)), ) }), ) }, }) /** * Expose domain resources */ return { query: { CountQuery, }, command: { SetCountCommand, IncreaseCountCommand, DecreaseCountCommand, StartCommand, StopCommand, }, event: { StartEvent, StopEvent, CountChangedEvent, }, } }, }) ``` -------------------------------- ### Accessing and Composing Domains in Remesh Source: https://context7.com/remesh-js/remesh/llms.txt Demonstrates how to access and compose with other domains for a modular architecture. Use `domain.getDomain` to access another domain's queries and commands. Remember to release domain references when no longer needed using `domain.forgetDomain`. ```typescript import { Remesh } from 'remesh' const AuthDomain = Remesh.domain({ name: 'AuthDomain', impl: (domain) => { const UserState = domain.state<{ id: string; name: string } | null>({ name: 'UserState', default: null, }) const UserQuery = domain.query({ name: 'UserQuery', impl: ({ get }) => get(UserState()), }) const IsLoggedInQuery = domain.query({ name: 'IsLoggedInQuery', impl: ({ get }) => get(UserState()) !== null, }) const LoginCommand = domain.command({ name: 'LoginCommand', impl: ({}, user: { id: string; name: string }) => UserState().new(user), }) return { query: { UserQuery, IsLoggedInQuery }, command: { LoginCommand }, } }, }) const TodoDomain = Remesh.domain({ name: 'TodoDomain', impl: (domain) => { // Access another domain const authDomain = domain.getDomain(AuthDomain()) const TodosState = domain.state({ name: 'TodosState', default: [] as string[] }) // Use query from another domain const UserTodosQuery = domain.query({ name: 'UserTodosQuery', impl: ({ get }) => { const user = get(authDomain.query.UserQuery()) const todos = get(TodosState()) return user ? { user: user.name, todos } : null }, }) const AddTodoCommand = domain.command({ name: 'AddTodoCommand', impl: ({ get }, title: string) => { // Check auth before adding if (!get(authDomain.query.IsLoggedInQuery())) { return null // Do nothing if not logged in } return TodosState().new([...get(TodosState()), title]) }, }) // Release domain reference when no longer needed const ForgetAuthCommand = domain.command({ name: 'ForgetAuthCommand', impl: ({}) => { domain.forgetDomain(AuthDomain()) return null }, }) return { query: { UserTodosQuery }, command: { AddTodoCommand, ForgetAuthCommand }, } }, }) ``` -------------------------------- ### Define Timer Effect with RxJS Source: https://context7.com/remesh-js/remesh/llms.txt This effect listens to start and stop events to manage a timer. It uses RxJS operators like `switchMap`, `interval`, `map`, and `takeUntil` to control the timer's behavior. ```typescript import { Remesh } from 'remesh' import { interval, fromEvent, merge } from 'rxjs' import { map, switchMap, takeUntil, debounceTime, filter } from 'rxjs/operators' const TimerDomain = Remesh.domain({ name: 'TimerDomain', impl: (domain) => { const CountState = domain.state({ name: 'CountState', default: 0 }) const StartEvent = domain.event({ name: 'StartEvent' }) const StopEvent = domain.event({ name: 'StopEvent' }) const TickCommand = domain.command({ name: 'TickCommand', impl: ({ get }) => CountState().new(get(CountState()) + 1), }) // Effect that listens to events domain.effect({ name: 'TimerEffect', impl: ({ fromEvent }) => { return fromEvent(StartEvent).pipe( switchMap(() => interval(1000).pipe( map(() => TickCommand()), takeUntil(fromEvent(StopEvent)) ) ) ) }, }) // Effect that listens to query changes const CountQuery = domain.query({ name: 'CountQuery', impl: ({ get }) => get(CountState()), }) const ThresholdExceededEvent = domain.event({ name: 'ThresholdExceededEvent', }) domain.effect({ name: 'ThresholdEffect', impl: ({ fromQuery }) => { return fromQuery(CountQuery()).pipe( filter((count) => count > 100), map((count) => ThresholdExceededEvent(count)) ) }, }) // Effect that combines multiple sources domain.effect({ name: 'CombinedEffect', impl: ({ fromEvent, fromQuery }) => { const event$ = fromEvent(StartEvent).pipe(map(() => TickCommand())) const query$ = fromQuery(CountQuery()).pipe( debounceTime(500), map(() => null) ) return merge(event$, query$) }, }) return { query: { CountQuery }, event: { StartEvent, StopEvent }, } }, }) ``` -------------------------------- ### Using Extern in a Remesh Domain Source: https://context7.com/remesh-js/remesh/llms.txt Inject and use an external dependency defined by Remesh.extern within a Remesh domain's implementation. This example shows persisting a counter's state to storage using effects and preloading data. ```typescript import { Remesh } from 'remesh' // Define extern interface type Storage = { get: (key: string) => Promise set: (key: string, value: T) => Promise clear: (key: string) => Promise } const StorageExtern = Remesh.extern({ default: null, }) // Use extern in domain const PersistentCounterDomain = Remesh.domain({ name: 'PersistentCounterDomain', impl: (domain) => { const storage = domain.getExtern(StorageExtern) const CountState = domain.state({ name: 'CountState', default: 0 }) const CountQuery = domain.query({ name: 'CountQuery', impl: ({ get }) => get(CountState()) }) const SetCountCommand = domain.command({ name: 'SetCountCommand', impl: ({}, count: number) => CountState().new(count), }) // Effect to persist count domain.effect({ name: 'PersistEffect', impl: ({ fromQuery }) => { return fromQuery(CountQuery()).pipe( switchMap(async (count) => { if (storage) { await storage.set('counter', count) } return null }) ) }, }) // Preload from storage on SSR domain.preload({ key: 'counter_preload', query: async () => { if (storage) { return (await storage.get('counter')) ?? 0 } return 0 }, command: ({}, count) => SetCountCommand(count), }) return { query: { CountQuery }, command: { SetCountCommand }, } }, }) ``` -------------------------------- ### Implement Server-Side Rendering with Remesh Source: https://context7.com/remesh-js/remesh/llms.txt Use domain preloading to fetch data on the server and hydrate the store on the client using RemeshRoot. ```typescript import { Remesh } from 'remesh' import { RemeshRoot } from 'remesh-react' // Domain with preload const DataDomain = Remesh.domain({ name: 'DataDomain', impl: (domain) => { const DataState = domain.state<{ items: string[] }>({ name: 'DataState', default: { items: [] }, }) const DataQuery = domain.query({ name: 'DataQuery', impl: ({ get }) => get(DataState()), }) const SetDataCommand = domain.command({ name: 'SetDataCommand', impl: ({}, data: { items: string[] }) => DataState().new(data), }) // Define preload for SSR domain.preload({ key: 'initial_data', query: async () => { const response = await fetch('https://api.example.com/data') return response.json() }, command: ({}, data) => SetDataCommand(data), }) return { query: { DataQuery }, command: { SetDataCommand }, } }, }) // Server-side (Next.js example) export async function getServerSideProps() { const store = Remesh.store() // Preload domain data await store.preload(DataDomain()) // Get preloaded state to send to client const preloadedState = store.getPreloadedState() return { props: { preloadedState }, } } // Client-side hydration export default function Page({ preloadedState }) { return ( ) } // Or create store with preloaded state directly const clientStore = Remesh.store({ preloadedState, }) ``` -------------------------------- ### Implementing and Using Remesh Extern Source: https://context7.com/remesh-js/remesh/llms.txt Provide a concrete implementation for a Remesh extern and create a Remesh store that includes this implementation. This allows domains to utilize the external dependency. ```typescript // Implement extern import localforage from 'localforage' const StorageImpl = StorageExtern.impl({ get: (key) => localforage.getItem(key), set: async (key, value) => { await localforage.setItem(key, value) }, clear: (key) => localforage.removeItem(key), }) // Create store with extern const store = Remesh.store({ externs: [StorageImpl], }) ``` -------------------------------- ### Integrate SSR with Next.js Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Use store.preload and store.getPreloadedState within getServerSideProps to hydrate the Remesh store on the server. ```tsx export type Props = { preloadedState: PreloadedState } export async function getServerSideProps(_context: NextPageContext) { // create remesh-store. const store = Remesh.store() // preload remesh domain await store.preload(PreloadDomain()) // get preloaded state const preloadedState = store.getPreloadedState() return { props: { preloadedState: preloadedState, }, // will be passed to the page component as props } } export default (props: Props) => { return ( ) } // or pass to remesh-store' directly const store = Remesh.store({ preloadedState, }) ``` -------------------------------- ### Sending Multiple Commands Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Pass an array of commands to store.send to execute them simultaneously. ```typescript import { Remesh } from 'Remesh' import YourDomain from 'your-domain' const store = Remesh.store() const yourDomain = store.getDomain(YourDomain()) // bundle commands or events into one array store.send([yourDomain.command.YourACommand('Hello, ACommand!'), yourDomain.command.YourBCommand('Hello, BCommand!')]) ``` -------------------------------- ### Define Remesh Domain and Extract Types Source: https://context7.com/remesh-js/remesh/llms.txt Demonstrates how to define a Remesh domain with queries, commands, and events, and then extract its type using `DomainTypeOf`. This is useful for strongly typing domain interactions. ```typescript import { Remesh, DomainTypeOf, ToType } from 'remesh' const MyDomain = Remesh.domain({ name: 'MyDomain', impl: (domain) => { const CountQuery = domain.query({ name: 'CountQuery', impl: () => 0, }) const IncrementCommand = domain.command({ name: 'IncrementCommand', impl: () => null, }) const ChangedEvent = domain.event({ name: 'ChangedEvent' }) return { query: { CountQuery }, command: { IncrementCommand }, event: { ChangedEvent }, } }, }) // Get domain return type type MyDomainType = DomainTypeOf // MyDomainType['query'] = { CountQuery: ... } // MyDomainType['command'] = { IncrementCommand: ... } // MyDomainType['event'] = { ChangedEvent: ... } // Convert interface to type (avoids TypeScript errors) interface UserInterface { id: string name: string nested: { value: number } } type UserType = ToType // Now safe to use with Remesh states ``` -------------------------------- ### Enable Real-time Collaboration with RemeshYjs Source: https://context7.com/remesh-js/remesh/llms.txt Synchronize domain state across clients using Yjs and a provider like WebrtcProvider. ```typescript import { Remesh } from 'remesh' import { RemeshYjs, RemeshYjsExtern } from 'remesh-yjs' import * as Y from 'yjs' import { WebrtcProvider } from 'y-webrtc' type SyncedState = { todos: { id: string; title: string; completed: boolean }[] filter: 'all' | 'active' | 'completed' } const CollaborativeTodoDomain = Remesh.domain({ name: 'CollaborativeTodoDomain', impl: (domain) => { const TodosState = domain.state({ name: 'TodosState', default: [], }) const FilterState = domain.state({ name: 'FilterState', default: 'all', }) const TodosQuery = domain.query({ name: 'TodosQuery', impl: ({ get }) => get(TodosState()), }) const FilterQuery = domain.query({ name: 'FilterQuery', impl: ({ get }) => get(FilterState()), }) const SetTodosCommand = domain.command({ name: 'SetTodosCommand', impl: ({}, todos: SyncedState['todos']) => TodosState().new(todos), }) const SetFilterCommand = domain.command({ name: 'SetFilterCommand', impl: ({}, filter: SyncedState['filter']) => FilterState().new(filter), }) // Setup Yjs synchronization RemeshYjs(domain, { key: 'collaborative-todos', dataType: 'object', onSend: ({ get }): SyncedState => ({ todos: get(TodosQuery()), filter: get(FilterQuery()), }), onReceive: ({ get }, state: SyncedState) => [ SetTodosCommand(state.todos), SetFilterCommand(state.filter), ], }) return { query: { TodosQuery, FilterQuery }, command: { SetTodosCommand, SetFilterCommand }, } }, }) // Setup Yjs document and provider const doc = new Y.Doc() const provider = new WebrtcProvider('my-room', doc) const yjsExternImpl = RemeshYjsExtern.impl({ doc }) const store = Remesh.store({ externs: [yjsExternImpl], }) store.igniteDomain(CollaborativeTodoDomain()) ``` -------------------------------- ### Subscribing to Events and Queries in Effects Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Use fromEvent and fromQuery within a domain effect to create observables that react to domain changes. ```typescript import { Remesh } from 'Remesh' import { merge } from 'rxjs' import { map } from 'rxjs/operators' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const YourQuery = domain.query({ name: 'YourQuery', impl: ({ get }) => get(YourState()), }) const YourCommand = domain.command({ name: 'YourCommand', impl: ({}, current: string) => { return YourState().new(current) }, }) const YourEvent = domain.event({ name: 'YourEvent', impl: ({ get }) => get(YourState()), }) domain.effect({ name: 'YourEffect', impl: ({ get, fromEvent, fromQuery }) => { /** * Subscribe to events via fromEvent(..) * The observable it returned will emit next value when the event is emitted. */ */ const event$ = fromEvent(YourEvent()) /** * Subscribe to queries via fromQuery(..) * The observable it returned will emit next value when the query is re-computed. */ const query$ = fromQuery(YourQuery()) return merge(event$, query$).pipe(map(() => [ACommand(), BCommand()])) } }) return { query: { YourQuery, }, command: { YourCommand, }, event: { YourEvent, }, } }, }) ``` -------------------------------- ### Creating Reusable Modules with Remesh.module Source: https://context7.com/remesh-js/remesh/llms.txt Defines and utilizes reusable modules for common patterns like text input fields. Modules encapsulate state, queries, commands, and events, promoting code reuse across different domains. Options can be passed to customize module behavior. ```typescript import { Remesh, RemeshDomainContext, Capitalize } from 'remesh' // Define module options type TextModuleOptions = { name: Capitalize default?: string maxLength?: number } // Create reusable module const TextModule = (domain: RemeshDomainContext, options: TextModuleOptions) => { const TextState = domain.state({ name: `${options.name}.TextState`, default: options.default ?? '', }) const TextQuery = domain.query({ name: `${options.name}.TextQuery`, impl: ({ get }) => get(TextState()), }) const LengthQuery = domain.query({ name: `${options.name}.LengthQuery`, impl: ({ get }) => get(TextState()).length, }) const SetTextCommand = domain.command({ name: `${options.name}.SetTextCommand`, impl: ({}, text: string) => { const finalText = options.maxLength ? text.slice(0, options.maxLength) : text return TextState().new(finalText) }, }) const ClearTextCommand = domain.command({ name: `${options.name}.ClearTextCommand`, impl: ({}) => TextState().new(''), }) const TextChangedEvent = domain.event({ name: `${options.name}.TextChangedEvent`, }) return Remesh.module({ query: { TextQuery, LengthQuery }, command: { SetTextCommand, ClearTextCommand }, event: { TextChangedEvent }, }) } // Use module in domain const FormDomain = Remesh.domain({ name: 'FormDomain', impl: (domain) => { const NameField = TextModule(domain, { name: 'NameField', maxLength: 50 }) const EmailField = TextModule(domain, { name: 'EmailField' }) const MessageField = TextModule(domain, { name: 'MessageField', maxLength: 500 }) const FormDataQuery = domain.query({ name: 'FormDataQuery', impl: ({ get }) => ({ name: get(NameField.query.TextQuery()), email: get(EmailField.query.TextQuery()), message: get(MessageField.query.TextQuery()), }), }) return { query: { FormDataQuery, NameQuery: NameField.query.TextQuery, EmailQuery: EmailField.query.TextQuery, MessageQuery: MessageField.query.TextQuery, }, command: { SetNameCommand: NameField.command.SetTextCommand, SetEmailCommand: EmailField.command.SetTextCommand, SetMessageCommand: MessageField.command.SetTextCommand, ClearFormCommand: domain.command({ name: 'ClearFormCommand', impl: ({}) => [ NameField.command.ClearTextCommand(), EmailField.command.ClearTextCommand(), MessageField.command.ClearTextCommand(), ], }), }, } }, }) ``` -------------------------------- ### Implement SSR Preloading in a Remesh Domain Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Define data fetching logic within a domain using domain.preload to prepare state for server-side rendering. ```tsx import { Remesh } from 'remesh' const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) export type State = { count: number } export const PreloadDomain = Remesh.domain({ name: 'PreloadDomain', impl: (domain) => { const CountState = domain.state({ name: 'CountState', default: { count: 0, }, }) const CountQuery = domain.query({ name: 'CountQuery', impl: ({ get }) => { return get(CountState()) }, }) const SetCountCommand = domain.command({ name: 'SetCountCommand', impl: ({}, newCount: number) => { return CountState().new({ count: newCount }) }, }) const IncreCommand = domain.command({ name: 'IncreCommand', impl: ({ get }) => { const state = get(CountState()) return CountState().new({ count: state.count + 1 }) }, }) const DecreCommand = domain.command({ name: 'DecreCommand', impl: ({ get }) => { const state = get(CountState()) return CountState().new({ count: state.count - 1 }) }, }) // define how to fetch data via domain.preload domain.preload({ key: 'preload_count', query: async () => { await delay(500) return { count: Math.floor(Math.random() * 100), } }, command: ({}, data) => { return CountState().new({ count: data.count }) }, }) return { query: { CountQuery, }, command: { SetCountCommand: SetCountCommand, IncreCommand: IncreCommand, DecreCommand: DecreCommand, }, } }, }) ``` -------------------------------- ### Implement undo/redo with HistoryModule Source: https://context7.com/remesh-js/remesh/llms.txt Use HistoryModule to track state changes and enable time-travel functionality within a domain. ```typescript import { Remesh } from 'remesh' import { HistoryModule } from 'remesh/modules/history' type EditorState = { content: string; cursorPosition: number } const EditorDomain = Remesh.domain({ name: 'EditorDomain', impl: (domain) => { const EditorState = domain.state({ name: 'EditorState', default: { content: '', cursorPosition: 0 }, }) const EditorQuery = domain.query({ name: 'EditorQuery', impl: ({ get }) => get(EditorState()), }) const SetEditorCommand = domain.command({ name: 'SetEditorCommand', impl: ({}, state: EditorState) => EditorState().new(state), }) const EditorHistory = HistoryModule(domain, { name: 'EditorHistory', query: ({ get }) => get(EditorQuery()), command: ({}, state) => SetEditorCommand(state), maxLength: 50, // Keep last 50 states }) return { query: { EditorQuery, CanUndoQuery: EditorHistory.query.CanBackQuery, CanRedoQuery: EditorHistory.query.CanForwardQuery, HistoryLengthQuery: domain.query({ name: 'HistoryLengthQuery', impl: ({ get }) => get(EditorHistory.query.HistoryListQuery()).length, }), }, command: { SetEditorCommand, UndoCommand: EditorHistory.command.BackCommand, RedoCommand: EditorHistory.command.ForwardCommand, GoCommand: EditorHistory.command.GoCommand, }, event: { UndoEvent: EditorHistory.event.BackEvent, RedoEvent: EditorHistory.event.ForwardEvent, }, } }, }) ``` -------------------------------- ### Pass Arguments to Domain Command Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Define commands that accept arguments by adding parameters to the impl function. ```typescript import { Remesh } from 'remesh' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const YourCommand = domain.command({ name: 'YourCommand', impl: ({ get }, arg: number) => { // do something }, }) }, }) ``` -------------------------------- ### Use a Custom Module in a Domain Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Integrate a custom module into a domain by passing the domain context to the module factory. ```typescript import { Remesh } from 'Remesh' import { TextModule } from 'my-custom-module' const MyDomain = Remesh.domain({ name: 'MyDomain', impl: (domain) => { /** * Passing domain as fixed argument. */ const Text = TextModule(domain, { name: 'Text', default: 'Hello, world!', }) return { command: { SetTextCommand: Text.command.SetTextCommand, ClearTextCommand: Text.command.ClearTextCommand, ResetCommand: Text.command.ResetCommand, }, event: { TextChangedEvent: Text.event.TextChangedEvent, }, } }, }) ``` -------------------------------- ### Fetch Async Resources in a Domain Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Use AsyncModule to handle asynchronous data fetching and lifecycle events within a domain. ```typescript import { Remesh } from 'remesh' import { AsyncModule } from 'remesh/modules/async' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const YourAsyncTask = AsyncModule(domain, { name: 'YourAsyncTask', load: async ({ get }, arg: number) => { const response = fetch('/path/to/api?arg=' + arg) const json = await response.json() return json }, onSuccess: ({ get }, json, arg) => { return MySuccessCommand(json) }, onFailed: ({ get }, error, arg) => { return MyFailedCommand(error.message) }, onLoading: ({ get }, arg) => { return MyLoadingCommand() }, onCanceled: ({ get }, arg) => { return MyCanceledCommand() }, onChanged: ({ get }, asyncState, arg) => { return MyChangedCommand() }, }) return { command: { LoadCommand: YourAsyncTask.command.LoadCommand, CancelCommand: YourAsyncTask.command.CancelCommand, ReloadCommand: YourAsyncTask.command.ReloadCommand, }, event: { SuccessEvent: YourAsyncTask.event.SuccessEvent, FailedEvent: YourAsyncTask.event.FailedEvent, LoadingEvent: YourAsyncTask.event.LoadingEvent, CanceledEvent: YourAsyncTask.event.CanceledEvent, ChangedEvent: YourAsyncTask.event.ChangedEvent, }, } }, }) ``` -------------------------------- ### Implement Time-Travel Functionality with HistoryModule Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Integrate the `HistoryModule` to enable undo/redo and time-travel capabilities. Configure it by providing a query to track state and a command to update it. ```typescript // use history-module in remesh import { HistoryModule } from 'remesh/modules/history' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const TodoAppHistoryModule = HistoryModule(domain, { name: 'TodoAppHistoryModule', // subscribe state via query query: ({ get }) => { return get(TodoAppStateQuery()) }, // sync state via command command: ({}, state) => { return UpdateTodoAppStateCommand(state) }, }) return { query: { // history list: T[] HistoryListQuery: TodoAppHistoryModule.query.HistoryListQuery, // can back: boolean CanBackQuery: TodoAppHistoryModule.query.CanBackQuery, // can forward: boolean CanForwardQuery: TodoAppHistoryModule.query.CanForwardQuery, // current index: number | null CurrentIndexQuery: TodoAppHistoryModule.query.CurrentIndexQuery, // current state: T | null CurrentStateQuery: TodoAppHistoryModule.query.CurrentStateQuery, }, command: { // go(n), n can be negative, just like history.go(n) GoCommand: TodoAppHistoryModule.command.GoCommand, // add state to history list AddCommand: TodoAppHistoryModule.command.AddCommand, // set history list SetCommand: TodoAppHistoryModule.command.SetCommand, // replace state ReplaceCommand: TodoAppHistoryModule.command.ReplaceCommand, // back() if possible BackCommand: TodoAppHistoryModule.command.BackCommand, // forward() if possible ForwardCommand: TodoAppHistoryModule.command.ForwardCommand, }, event: { // trigger when back BackEvent: TodoAppHistoryModule.event.BackEvent, // trigger when forward ForwardEvent: TodoAppHistoryModule.event.ForwardEvent, // trigger when go GoEvent: TodoAppHistoryModule.event.GoEvent, }, } }, }) ``` -------------------------------- ### Configure Debugging with RemeshLogger Source: https://context7.com/remesh-js/remesh/llms.txt Track domain events, state changes, and commands by adding inspectors to the store. ```typescript import { Remesh } from 'remesh' import { RemeshLogger } from 'remesh-logger' import { RemeshReduxDevtools } from 'remesh-redux-devtools' // Create store with logger const store = Remesh.store({ name: 'MyApp', inspectors: [ // Console logger RemeshLogger({ collapsed: true, // Collapse log groups include: { domain: true, event: true, state: true, query: true, command: true, }, }), // Redux DevTools integration RemeshReduxDevtools(), ], }) // Logs will show: // - Domain creation/destruction // - State updates with old/new values // - Query computations // - Command executions // - Event emissions ``` -------------------------------- ### Pass Arguments to Domain Query Source: https://github.com/remesh-js/remesh/blob/main/docs/how-to-guide.md Define queries that accept arguments by adding parameters to the impl function. ```typescript import { Remesh } from 'remesh' const YourDomain = Remesh.domain({ name: 'YourDomain', impl: (domain) => { const YourQuery = domain.query({ name: 'YourQuery', impl: ({ get }, arg: number) => { // do something }, }) }, }) ```