From 37dbc2e5f410fc51d6be8774e60afd35e53099fe Mon Sep 17 00:00:00 2001 From: Paul Phan Date: Thu, 27 Aug 2020 15:49:43 +0700 Subject: [PATCH] update mechanism --- example/index.tsx | 6 ++++-- package.json | 18 ++++++++-------- src/index.ts | 52 ++++++++++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 32 deletions(-) diff --git a/example/index.tsx b/example/index.tsx index 3a71ebb..8c01a0c 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -1,6 +1,8 @@ +// @ts-ignore import React, { ChangeEvent, useContext, useEffect } from "react" import { createSubscription, + ISubscription, useReducerSubscription, useSubscription, } from "../src/index" @@ -42,7 +44,7 @@ function FooDisplay() { } const useTextValue = () => { - const textSubscription = useContext(TextContext) + const textSubscription = useContext>(TextContext) let { state, setState } = useSubscription(textSubscription) const onChange = (e: ChangeEvent) => setState(e.target.value) @@ -58,7 +60,7 @@ function Text() { ) } -const TextContext = React.createContext(null) +const TextContext = React.createContext>(null) function TextComponent() { const textSubscription = createSubscription("The text will sync together") diff --git a/package.json b/package.json index d053aa6..f4a788c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "global-state-hook", - "version": "1.5.1", + "version": "1.6.0", "description": "Super tiny state sharing library with hooks", "source": "src/index.ts", "main": "dist/index.js", @@ -25,17 +25,17 @@ }, "license": "MIT", "devDependencies": { - "@types/react": "^16.9.34", - "@types/react-dom": "^16.9.7", + "@types/react": "^16.9.48", + "@types/react-dom": "^16.9.8", "husky": "^4.2.5", - "lint-staged": "^10.2.2", - "microbundle": "^0.11.0", + "lint-staged": "^10.2.13", + "microbundle": "^0.12.3", "parcel": "^1.12.4", - "prettier": "^2.0.5", + "prettier": "^2.1.1", "react": "^16.13.1", "react-dom": "^16.13.1", - "ts-node": "^8.10.1", - "typescript": "^3.8.3" + "ts-node": "^9.0.0", + "typescript": "^4.0.2" }, "husky": { "hooks": { @@ -43,7 +43,7 @@ } }, "lint-staged": { - "*": [ + "*.{js,jsx,ts,tsx}": [ "prettier --write", "git add" ] diff --git a/src/index.ts b/src/index.ts index fb51d2f..143149d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ type Listener = (newState: S) => void export interface ISubscription { subscribe: (fn: Listener) => void unsubscribe: (fn: Listener) => void - listener: Listener[] + listener: Set> state: S updateState: (nextState: S, forceUpdate?: boolean) => void } @@ -14,54 +14,62 @@ export function createSubscription( initialState?: S, ): ISubscription { const state: S = initialState || ({} as any) - let listener: Listener[] = [] - const subscribe = (fn: Listener) => listener.push(fn) - const unsubscribe = (fn: Listener) => - (listener = listener.filter((f) => f !== fn)) + let listener: Set> = new Set() + const subscribe = (fn: Listener) => { + listener.add(fn) + } + const unsubscribe = (fn: Listener) => listener.delete(fn) const updateState = (nextState: S, forceUpdate = true) => { Object.assign(state, nextState) - forceUpdate && listener.forEach((fn) => fn(nextState)) + if (forceUpdate) { + listener.forEach((fn) => fn(nextState)) + } } return { subscribe, unsubscribe, listener, state, updateState } } export interface IStateUpdater { + changed?: any setState: (newState: any, callback?: (newState: S) => void) => void state: S } + export interface IStateReduceUpdater { dispatch: (p: any) => void state: S } -function useSubscriber( + +function useSubscriber( subscriber: ISubscription, pick?: string[], ) { const [changed, setUpdate] = React.useState({}) - const mounted = React.useRef(true) - const updater = React.useCallback((nextState: S) => { - if ( - mounted.current && - (!pick || + // const mounted = React.useRef(true) + const updater = React.useCallback( + (nextState: S) => { + if ( + !pick || !pick.length || typeof nextState !== "object" || nextState.constructor !== Object || - Object.keys(nextState).find((k) => pick.includes(k))) - ) { - setUpdate({}) - } - }, []) + Object.keys(nextState).find((k) => pick.includes(k)) + ) { + setUpdate({}) + } + }, + [pick], + ) React.useEffect(() => { subscriber.subscribe(updater) return () => { - mounted.current = false subscriber.unsubscribe(updater) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []) return changed } -export function useReducerSubscription( +export function useReducerSubscription( subscriber: ISubscription, reducer: any = () => {}, ): IStateReduceUpdater { @@ -76,13 +84,14 @@ export function useReducerSubscription( return { state: subscriber.state, dispatch } } -export function useSubscription( +export function useSubscription( subscriber: ISubscription, pick?: string[], ): IStateUpdater { - useSubscriber(subscriber, pick) + const changed = useSubscriber(subscriber, pick) React.useDebugValue(subscriber.state) return { + changed, state: subscriber.state, setState: React.useCallback( (newState: any, callback?: Function) => { @@ -97,6 +106,7 @@ export function useSubscription( subscriber.listener.forEach((fn) => fn(newState)) callback && callback() }, + // eslint-disable-next-line react-hooks/exhaustive-deps [subscriber.state, pick], ), }