UNPKG

@guanghechen/react-viewmodel

Version:
250 lines (203 loc) 7.02 kB
<header> <h1 align="center"> <a href="https://github.com/guanghechen/react-kit/tree/@guanghechen/react-viewmodel@0.2.18/packages/react-viewmodel#readme">@guanghechen/react-viewmodel</a> </h1> <div align="center"> <a href="https://www.npmjs.com/package/@guanghechen/react-viewmodel"> <img alt="Npm Version" src="https://img.shields.io/npm/v/@guanghechen/react-viewmodel.svg" /> </a> <a href="https://www.npmjs.com/package/@guanghechen/react-viewmodel"> <img alt="Npm Download" src="https://img.shields.io/npm/dm/@guanghechen/react-viewmodel.svg" /> </a> <a href="https://www.npmjs.com/package/@guanghechen/react-viewmodel"> <img alt="Npm License" src="https://img.shields.io/npm/l/@guanghechen/react-viewmodel.svg" /> </a> <a href="#install"> <img alt="Module Formats: cjs, esm" src="https://img.shields.io/badge/module_formats-cjs%2C%20esm-green.svg" /> </a> <a href="https://github.com/nodejs/node"> <img alt="Node.js Version" src="https://img.shields.io/node/v/@guanghechen/react-viewmodel" /> </a> <a href="https://github.com/vitest-dev/vitest"> <img alt="Tested with Vitest" src="https://img.shields.io/badge/tested_with-vitest-6E9F18.svg" /> </a> <a href="https://github.com/prettier/prettier"> <img alt="Code Style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" /> </a> </div> </header> <br/> React viewmodel integration providing reactive state management with State and Computed patterns. ## Install * npm ```bash npm install --save @guanghechen/react-viewmodel ``` * pnpm ```bash pnpm add @guanghechen/react-viewmodel ``` ## Usage ### Basic State Management Create reactive state with `State` and consume it with `useStateValue`: ```tsx import { State, useStateValue } from '@guanghechen/react-viewmodel' import React from 'react' // Create a state with recommended '$' suffix naming const count$ = new State<number>(0) const Counter: React.FC = () => { // Use useStateValue to subscribe to state changes const count = useStateValue(count$) const increment = () => count$.setState(prev => prev + 1) const decrement = () => count$.setState(prev => prev - 1) return ( <div> <p>Count: {count}</p> <button onClick={increment}>+</button> <button onClick={decrement}>-</button> </div> ) } ``` ### Computed Values Create derived state with `Computed`: ```tsx import { State, Computed, useStateValue, useComputed } from '@guanghechen/react-viewmodel' import React from 'react' const firstName$ = new State<string>('John') const lastName$ = new State<string>('Doe') // Create computed value with '$' suffix const fullName$ = new Computed(() => { return `${firstName$.getSnapshot()} ${lastName$.getSnapshot()}` }) const UserProfile: React.FC = () => { const firstName = useStateValue(firstName$) const lastName = useStateValue(lastName$) const fullName = useComputed(fullName$) return ( <div> <input value={firstName} onChange={e => firstName$.setState(() => e.target.value)} placeholder="First name" /> <input value={lastName} onChange={e => lastName$.setState(() => e.target.value)} placeholder="Last name" /> <p>Full name: {fullName}</p> </div> ) } ``` ### Complex State with Multiple Dependencies ```tsx import { State, Computed, useStateValue, useComputed } from '@guanghechen/react-viewmodel' import React from 'react' interface ICartItem { id: string name: string price: number quantity: number } const cartItems$ = new State<ICartItem[]>([]) const taxRate$ = new State<number>(0.1) // Computed total price including tax const totalPrice$ = new Computed(() => { const items = cartItems$.getSnapshot() const tax = taxRate$.getSnapshot() const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0) return subtotal * (1 + tax) }) const ShoppingCart: React.FC = () => { const items = useStateValue(cartItems$) const total = useComputed(totalPrice$) const addItem = (item: ICartItem) => { cartItems$.setState(prev => [...prev, item]) } return ( <div> <ul> {items.map(item => ( <li key={item.id}> {item.name} - ${item.price} x {item.quantity} </li> ))} </ul> <p>Total: ${total.toFixed(2)}</p> </div> ) } ``` ### ViewModel Management Create and manage ViewModel instances with automatic lifecycle management: ```tsx import { ViewModel, useViewModel } from '@guanghechen/react-viewmodel' import React from 'react' class FileTreeViewModel extends ViewModel { constructor(public readonly options: { currentFilepath: string | null }) { super() } // Your ViewModel implementation } interface IFileTreeContext { viewmodel: FileTreeViewModel } const FileTreeContextType = React.createContext<IFileTreeContext | null>(null) export const FileTreeContextProvider: React.FC<{ children: React.ReactNode }> = props => { const viewmodel: FileTreeViewModel | null = useViewModel<FileTreeViewModel>(() => { return new FileTreeViewModel({ currentFilepath: null }) }) const context: IFileTreeContext | null = React.useMemo<IFileTreeContext | null>( () => (viewmodel ? { viewmodel } : null), [viewmodel], ) if (!context) return <React.Fragment /> return ( <FileTreeContextType.Provider value={context}>{props.children}</FileTreeContextType.Provider> ) } FileTreeContextProvider.displayName = 'FileTreeContextProvider' ``` ## API Reference Name | Description :------------------------:|:---------------------------------------------: `useStateValue` | Subscribe to state changes and get current value `useSetState` | Get state setter function only `useState` | Get both state value and setter (similar to React.useState) `useReactState` | Get state value and updater with React-like API `useComputed` | Subscribe to computed value changes `useToggleState` | Get toggle function for boolean state `useObserveKey` | Observe specific object property changes `useObserveSignal` | Observe signal changes with custom comparator `useViewModel` | Create and manage ViewModel instances with automatic disposal ## Naming Convention It is recommended to suffix State and Computed instances with `$` to clearly indicate they are reactive values: ```tsx const count$ = new State<number>(0) const doubleCount$ = new Computed(() => count$.getSnapshot() * 2) const user$ = new State<IUser>({ name: 'Alice', age: 30 }) ``` [homepage]: https://github.com/guanghechen/react-kit/tree/@guanghechen/react-viewmodel@0.2.18/packages/react-viewmodel#readme