@statx/react
Advanced tools
+6
-6
| { | ||
| "name": "@statx/react", | ||
| "version": "1.13.3", | ||
| "version": "1.15.1", | ||
| "private": false, | ||
@@ -53,6 +53,6 @@ "description": "Extry tiny smart statx manager", | ||
| "dependencies": { | ||
| "@statx/core": "^1.13.3" | ||
| "@statx/core": "^1.15.1" | ||
| }, | ||
| "peerDependencies": { | ||
| "react": "^16.0.0" | ||
| "react": "^18.0.0" | ||
| }, | ||
@@ -69,7 +69,7 @@ "targets": { | ||
| }, | ||
| "gitHead": "71a4ae94e04a436353e1511d1db7d84b18f061c2", | ||
| "gitHead": "8032cbbc175aade084789299df965673d922a2bb", | ||
| "devDependencies": { | ||
| "@types/react-dom": "^16.0.0", | ||
| "react-dom": "^16.0.0" | ||
| "@types/react-dom": "^18.0.0", | ||
| "react-dom": "^18.0.0" | ||
| } | ||
| } |
+41
-15
@@ -1,23 +0,45 @@ | ||
| import {useStatxComp, useStatx} from '../index.js' | ||
| import {state} from '@statx/core' | ||
| /* eslint-disable @typescript-eslint/ban-types */ | ||
| import {createRoot} from 'react-dom/client' | ||
| import {StrictMode, useEffect, useState} from 'react' | ||
| import React from 'react' | ||
| import {statxComponent} from '../index' | ||
| import {computed} from '@statx/core' | ||
| const nameObject = state({name: 'John'}) | ||
| const Test = statxComponent<{time: number}>(({time}) => { | ||
| console.log('init Test') | ||
| setInterval(() => { | ||
| nameObject.set({name: Date.now().toString()}) | ||
| }, 1) | ||
| const isMoreThen10 = computed(() => { | ||
| console.log('isMoreThen10') | ||
| return time() > 10 | ||
| }) | ||
| let rendersCount = 0 | ||
| const testData = computed(() => { | ||
| if (!isMoreThen10()) { | ||
| return 'Меньше 10' | ||
| } | ||
| return 'Больше 10' | ||
| }) | ||
| return () => { | ||
| return ( | ||
| <> | ||
| <h1>SimpleComponent</h1> | ||
| <h2>time: {time()}</h2> | ||
| <p>{testData()}</p> | ||
| </> | ||
| ) | ||
| } | ||
| }, 'Test') | ||
| const App = () => { | ||
| rendersCount++ | ||
| const name = useStatxComp(nameObject, (data) => { | ||
| return <input value={data.name} /> | ||
| }) | ||
| const [v, setV] = useState(0) | ||
| useEffect(() => { | ||
| const timer = setInterval(() => { | ||
| setV((v) => v + 1) | ||
| }, 1000) | ||
| return () => clearInterval(timer) | ||
| }, []) | ||
| return ( | ||
| <> | ||
| <h1>Renders: {rendersCount}</h1> | ||
| <h2>Time: {name}</h2> | ||
| <Test time={v} /> | ||
| </> | ||
@@ -28,2 +50,6 @@ ) | ||
| const root = createRoot(document.getElementById('root') as HTMLDivElement) | ||
| root.render(<App />) | ||
| root.render( | ||
| <StrictMode> | ||
| <App /> | ||
| </StrictMode>, | ||
| ) |
+52
-29
@@ -1,35 +0,58 @@ | ||
| import type {ReactElement, ReactNode} from 'react' | ||
| import {Fragment, useMemo, createElement, useEffect, useState} from 'react' | ||
| import type {PublicState, StateType} from '@statx/core' | ||
| /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
| import type {ReactNode} from 'react' | ||
| import {createElement, PureComponent} from 'react' | ||
| import type {State, Computed} from '@statx/core' | ||
| import {computed, state} from '@statx/core' | ||
| export const useStatx = <T extends StateType>(state: PublicState<T>): T => { | ||
| const [inner, setInner] = useState<T>(state()) | ||
| type Props = Record<string, unknown> | ||
| type PropsX = Record<string, State<unknown>> | ||
| useEffect(() => { | ||
| return state.subscribe(setInner) | ||
| }, []) | ||
| type ConvertedRecord<T extends Props> = { | ||
| [K in keyof T]: State<T[K]> | ||
| } | ||
| return inner | ||
| class StatxComponent<P extends {fn: () => Computed<unknown>}, S = Props> extends PureComponent<P, S> { | ||
| private unsub?: () => void | ||
| componentWillUnmount(): void { | ||
| this.unsub?.() | ||
| } | ||
| componentDidMount(): void { | ||
| this.unsub = this.props.fn?.().subscribe(() => this.forceUpdate()) | ||
| } | ||
| render() { | ||
| return this.props.fn()() | ||
| } | ||
| } | ||
| export const useSXComponent = <T extends StateType>( | ||
| state: PublicState<T>, | ||
| f?: (value: T) => ReactNode, | ||
| ): ReactElement => { | ||
| return useMemo(() => { | ||
| return createElement(() => { | ||
| const res = useStatx<T>(state) | ||
| if (!res) { | ||
| return null | ||
| } | ||
| if (f) { | ||
| const mappedResult = f(res) | ||
| return createElement(Fragment, {}, mappedResult) | ||
| } | ||
| if (typeof res === 'object') { | ||
| return createElement(Fragment, {}, JSON.stringify(res)) | ||
| } | ||
| return createElement(Fragment, {}, res.toString()) | ||
| }) | ||
| }, []) | ||
| const setStateProps = (newProps: Props, stateProps: PropsX) => { | ||
| Object.entries(newProps).map((item) => { | ||
| if (item[0] in stateProps) { | ||
| stateProps[item[0]].set(item[1]) | ||
| } else { | ||
| stateProps[item[0]] = state(item[1]) | ||
| } | ||
| }) | ||
| } | ||
| export const statxComponent = <T extends Props, K = ConvertedRecord<T>>( | ||
| initFn: (state: K) => () => ReactNode, | ||
| name?: string, | ||
| ) => { | ||
| let computedFn: Computed<unknown> | ||
| let inited = false | ||
| const stateProps = {} as K | ||
| const statxElement = createElement(StatxComponent as any, { | ||
| fn: () => computedFn, | ||
| }) | ||
| return (props: Props) => { | ||
| setStateProps(props, stateProps as PropsX) | ||
| if (!inited) { | ||
| computedFn = computed(initFn(stateProps), {name: `${name}.render`}) | ||
| inited = true | ||
| } | ||
| return statxElement | ||
| } | ||
| } |
9398
13.8%172
35.43%+ Added
- Removed
- Removed
- Removed
- Removed
Updated