From 08d8bb0ce40dfc94db577303e9105c01c76e3b35 Mon Sep 17 00:00:00 2001 From: chnliquan Date: Fri, 4 Jun 2021 10:36:42 +0800 Subject: [PATCH] feat: support with provider forward ref, closes #4 --- package-lock.json | 17 +++++++++---- package.json | 2 ++ src/core/Store.tsx | 59 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index cd717a3..6659f0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dobux", - "version": "1.0.4", + "version": "1.0.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1781,6 +1781,16 @@ "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", "dev": true }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dev": true, + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -6572,7 +6582,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dev": true, "requires": { "react-is": "^16.7.0" } @@ -11500,6 +11509,7 @@ "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "dev": true, "requires": { + "hoist-non-react-statics": "^3.3.2", "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2" @@ -11532,8 +11542,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-refresh": { "version": "0.9.0", diff --git a/package.json b/package.json index 5b557a1..9a60de5 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ } }, "dependencies": { + "hoist-non-react-statics": "^3.3.2", "immer": "^6.0.9" }, "peerDependencies": { @@ -79,6 +80,7 @@ "@testing-library/react": "^9.5.0", "@testing-library/react-hooks": "^3.4.2", "@testing-library/user-event": "^7.2.1", + "@types/hoist-non-react-statics": "^3.3.1", "@types/jest": "^26.0.15", "@types/node": "^14.14.8", "@types/react": "^16.8.0", diff --git a/src/core/Store.tsx b/src/core/Store.tsx index 0e2027c..334cfe7 100644 --- a/src/core/Store.tsx +++ b/src/core/Store.tsx @@ -1,9 +1,19 @@ -import React from 'react' +import React, { PropsWithoutRef, RefAttributes } from 'react' +import hoistNonReactStatics from 'hoist-non-react-statics' import { Model } from './Model' import { isObject, isArray, isUndefined, isNull, isFunction } from '../utils/type' import { invariant } from '../utils/invariant' -import { Configs, StoreOptions, ModelConfig, StoreProvider, MapStateToModel, Models, ModelState, HOC } from '../types' +import { + Configs, + StoreOptions, + ModelConfig, + StoreProvider, + MapStateToModel, + Models, + ModelState, + HOC, +} from '../types' type StoreModels = { [K in keyof C]: Model> @@ -12,8 +22,8 @@ type StoreModels = { export class Store { private models: StoreModels private rootModel = Object.create(null) - - constructor(private configs: C, options: Required>) { + + constructor(private configs: C, options: Required>) { this.models = this.initModels(configs, options) this.getState = this.getState.bind(this) } @@ -30,7 +40,7 @@ export class Store { public withProvider = >( // eslint-disable-next-line @typescript-eslint/no-unused-vars - Component: React.ComponentType, + Component: React.ComponentType ) => { return (props: T): React.ReactElement => { return ( @@ -41,6 +51,31 @@ export class Store { } } + // https://stackoverflow.com/questions/61743517/what-is-the-right-way-to-use-forwardref-with-withrouter + public withProviderForwardRef = >( + Component: React.ForwardRefExoticComponent & RefAttributes> + ) => { + const WithProvider: React.FC = ({ forwardedRef, ...props }) => { + return ( + + + + ) + } + + const WithProviderForwardRef = React.forwardRef((props, ref) => ( + + )) + + const displayName = Component.displayName || Component.name + + WithProviderForwardRef.displayName = `${displayName}-with-provider-forwardRef` + + hoistNonReactStatics(WithProviderForwardRef, Component) + + return WithProviderForwardRef + } + public useModel = ( modelName: K, mapStateToModel: MapStateToModel[K], S> = (state: S): S => state @@ -49,7 +84,10 @@ export class Store { const modelNames = Object.keys(this.configs) - invariant(modelNames.indexOf(modelName as string) > -1, `[store.useModel] Expected the modelName to be one of ${modelNames}, but got ${modelName}`) + invariant( + modelNames.indexOf(modelName as string) > -1, + `[store.useModel] Expected the modelName to be one of ${modelNames}, but got ${modelName}` + ) invariant( isUndefined(mapStateToModel) || isFunction(mapStateToModel), @@ -77,7 +115,10 @@ export class Store { public getState(modelName?: K) { if (modelName) { const modelNames = Object.keys(this.configs) - invariant(modelNames.indexOf(modelName as string) > -1, `[store.getState] Expected the modelName to be one of ${modelNames}, but got ${modelName}`) + invariant( + modelNames.indexOf(modelName as string) > -1, + `[store.getState] Expected the modelName to be one of ${modelNames}, but got ${modelName}` + ) return this.rootModel[modelName].state } else { @@ -107,9 +148,9 @@ export class Store { const config = Object.create(null) config.state = isObject(state) ? { ...state } : isArray(state) ? [...state] : state - config.reducers = { ...reducers } + config.reducers = { ...reducers } config.effects = effects(config, this.rootModel) - + models[name] = new Model({ storeName, name,