@tramvai/state 2.70.0 → 2.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/connect/Provider.es.js +8 -0
- package/lib/connect/Provider.js +12 -0
- package/lib/connect/Subscription.es.js +37 -0
- package/lib/connect/Subscription.js +41 -0
- package/lib/connect/connectAdvanced.es.js +159 -0
- package/lib/connect/connectAdvanced.js +170 -0
- package/lib/connect/context.es.js +6 -0
- package/lib/connect/context.js +11 -0
- package/lib/connect/hooks/useActions.es.js +17 -0
- package/lib/connect/hooks/useActions.js +25 -0
- package/lib/connect/hooks/useConsumerContext.es.js +12 -0
- package/lib/connect/hooks/useConsumerContext.js +16 -0
- package/lib/connect/hooks/useSelector.es.js +53 -0
- package/lib/connect/hooks/useSelector.js +63 -0
- package/lib/connect/hooks/useStore.es.js +37 -0
- package/lib/connect/hooks/useStore.js +41 -0
- package/lib/connect/hooks/useStoreSelector.es.js +11 -0
- package/lib/connect/hooks/useStoreSelector.js +15 -0
- package/lib/connect/index.es.js +91 -0
- package/lib/connect/index.js +107 -0
- package/lib/connect/scheduling.es.js +12 -0
- package/lib/connect/scheduling.js +16 -0
- package/lib/connect/selectorFactory.es.js +95 -0
- package/lib/connect/selectorFactory.js +101 -0
- package/lib/connect/toProps/mapContextToProps.es.js +23 -0
- package/lib/connect/toProps/mapContextToProps.js +34 -0
- package/lib/connect/toProps/mapStateToProps.es.js +17 -0
- package/lib/connect/toProps/mapStateToProps.js +27 -0
- package/lib/connect/toProps/mergeProps.es.js +36 -0
- package/lib/connect/toProps/mergeProps.js +42 -0
- package/lib/connect/toProps/wrapMapToProps.es.js +73 -0
- package/lib/connect/toProps/wrapMapToProps.js +85 -0
- package/lib/connect/utils/verifyFunction.es.js +10 -0
- package/lib/connect/utils/verifyFunction.js +18 -0
- package/lib/connect/utils/verifyMapToProps.es.js +20 -0
- package/lib/connect/utils/verifyMapToProps.js +28 -0
- package/lib/connect/utils/verifyPlainObject.es.js +10 -0
- package/lib/connect/utils/verifyPlainObject.js +18 -0
- package/lib/createEvent/createEvent.es.js +22 -0
- package/lib/createEvent/createEvent.js +31 -0
- package/lib/createReducer/createReducer.es.js +60 -0
- package/lib/createReducer/createReducer.js +64 -0
- package/lib/devTools/constants.es.js +3 -0
- package/lib/devTools/constants.js +7 -0
- package/lib/{index_middleware.es.js → devTools/devTools.es.js} +2 -36
- package/lib/{index_middleware.js → devTools/devTools.js} +4 -40
- package/lib/devTools/index.es.js +21 -0
- package/lib/devTools/index.js +23 -0
- package/lib/devTools/middleware.es.js +37 -0
- package/lib/devTools/middleware.js +45 -0
- package/lib/dispatcher/childDispatcherContext.es.js +46 -0
- package/lib/dispatcher/childDispatcherContext.js +50 -0
- package/lib/dispatcher/dispatcher.es.js +105 -0
- package/lib/dispatcher/dispatcher.js +110 -0
- package/lib/dispatcher/dispatcherContext.es.js +279 -0
- package/lib/dispatcher/dispatcherContext.js +288 -0
- package/lib/dispatcher/storeSubscribe.es.js +8 -0
- package/lib/dispatcher/storeSubscribe.js +12 -0
- package/lib/index.es.js +16 -1246
- package/lib/index.js +36 -1281
- package/lib/logger.es.js +3 -0
- package/lib/logger.js +7 -0
- package/lib/stores/BaseStore.es.js +53 -0
- package/lib/stores/BaseStore.js +61 -0
- package/lib/stores/SimpleEmitter.es.js +30 -0
- package/lib/stores/SimpleEmitter.js +34 -0
- package/package.json +6 -7
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { ConnectContext, ServerStateContext } from './context.es.js';
|
|
3
|
+
|
|
4
|
+
const Provider = ({ context, children, serverState, }) => {
|
|
5
|
+
return (jsx(ConnectContext.Provider, { value: context, children: jsx(ServerStateContext.Provider, { value: serverState, children: children }) }));
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { Provider };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var context = require('./context.js');
|
|
7
|
+
|
|
8
|
+
const Provider = ({ context: context$1, children, serverState, }) => {
|
|
9
|
+
return (jsxRuntime.jsx(context.ConnectContext.Provider, { value: context$1, children: jsxRuntime.jsx(context.ServerStateContext.Provider, { value: serverState, children: children }) }));
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
exports.Provider = Provider;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// encapsulates the subscription logic for connecting a component to the redux store, as
|
|
2
|
+
// well as nesting subscriptions of descendant components, so that we can ensure the
|
|
3
|
+
// ancestor components re-render before descendants
|
|
4
|
+
class Subscription {
|
|
5
|
+
constructor(stores) {
|
|
6
|
+
this.handleStateChange = () => {
|
|
7
|
+
this.onStateChange && this.onStateChange();
|
|
8
|
+
};
|
|
9
|
+
this.stores = stores;
|
|
10
|
+
this.unsubscribe = undefined;
|
|
11
|
+
}
|
|
12
|
+
isSubscribed() {
|
|
13
|
+
return Boolean(this.unsubscribe);
|
|
14
|
+
}
|
|
15
|
+
setOnStateChange(onStateChange) {
|
|
16
|
+
this.onStateChange = onStateChange;
|
|
17
|
+
}
|
|
18
|
+
trySubscribe() {
|
|
19
|
+
if (!this.unsubscribe) {
|
|
20
|
+
this.unsubscribe = this.stores.filter(Boolean).map((store) => {
|
|
21
|
+
store.on('change', this.handleStateChange);
|
|
22
|
+
return () => {
|
|
23
|
+
store.removeListener('change', this.handleStateChange);
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
tryUnsubscribe() {
|
|
29
|
+
if (this.unsubscribe) {
|
|
30
|
+
this.unsubscribe.forEach((f) => f());
|
|
31
|
+
this.unsubscribe = undefined;
|
|
32
|
+
}
|
|
33
|
+
this.onStateChange = undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Subscription };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
// encapsulates the subscription logic for connecting a component to the redux store, as
|
|
6
|
+
// well as nesting subscriptions of descendant components, so that we can ensure the
|
|
7
|
+
// ancestor components re-render before descendants
|
|
8
|
+
class Subscription {
|
|
9
|
+
constructor(stores) {
|
|
10
|
+
this.handleStateChange = () => {
|
|
11
|
+
this.onStateChange && this.onStateChange();
|
|
12
|
+
};
|
|
13
|
+
this.stores = stores;
|
|
14
|
+
this.unsubscribe = undefined;
|
|
15
|
+
}
|
|
16
|
+
isSubscribed() {
|
|
17
|
+
return Boolean(this.unsubscribe);
|
|
18
|
+
}
|
|
19
|
+
setOnStateChange(onStateChange) {
|
|
20
|
+
this.onStateChange = onStateChange;
|
|
21
|
+
}
|
|
22
|
+
trySubscribe() {
|
|
23
|
+
if (!this.unsubscribe) {
|
|
24
|
+
this.unsubscribe = this.stores.filter(Boolean).map((store) => {
|
|
25
|
+
store.on('change', this.handleStateChange);
|
|
26
|
+
return () => {
|
|
27
|
+
store.removeListener('change', this.handleStateChange);
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
tryUnsubscribe() {
|
|
33
|
+
if (this.unsubscribe) {
|
|
34
|
+
this.unsubscribe.forEach((f) => f());
|
|
35
|
+
this.unsubscribe = undefined;
|
|
36
|
+
}
|
|
37
|
+
this.onStateChange = undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
exports.Subscription = Subscription;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import noop from '@tinkoff/utils/function/noop';
|
|
3
|
+
import hoistStatics from 'hoist-non-react-statics';
|
|
4
|
+
import invariant from 'invariant';
|
|
5
|
+
import React, { useMemo, useContext, useRef, useCallback } from 'react';
|
|
6
|
+
import { isValidElementType } from 'react-is';
|
|
7
|
+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
8
|
+
import { Subscription } from './Subscription.es.js';
|
|
9
|
+
import { useConsumerContext } from './hooks/useConsumerContext.es.js';
|
|
10
|
+
import '@tinkoff/utils/is/array';
|
|
11
|
+
import '@tinkoff/react-hooks';
|
|
12
|
+
import { ServerStateContext } from './context.es.js';
|
|
13
|
+
import '@tinkoff/utils/is/shallowEqual';
|
|
14
|
+
import '@tinkoff/utils/array/toArray';
|
|
15
|
+
import 'use-sync-external-store/shim/with-selector';
|
|
16
|
+
|
|
17
|
+
const stringifyComponent = (Comp) => {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.stringify(Comp);
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
return String(Comp);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated
|
|
27
|
+
*/
|
|
28
|
+
function connectAdvanced(
|
|
29
|
+
/*
|
|
30
|
+
selectorFactory is a func that is responsible for returning the selector function used to
|
|
31
|
+
compute new props from state, props, and dispatch. For example:
|
|
32
|
+
export default connectAdvanced((dispatch, options) => (state, props) => ({
|
|
33
|
+
thing: state.things[props.thingId],
|
|
34
|
+
saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)),
|
|
35
|
+
}))(YourComponent)
|
|
36
|
+
Access to dispatch is provided to the factory so selectorFactories can bind actionCreators
|
|
37
|
+
outside of their selector as an optimization. Options passed to connectAdvanced are passed to
|
|
38
|
+
the selectorFactory, along with displayName and WrappedComponent, as the second argument.
|
|
39
|
+
Note that selectorFactory is responsible for all caching/memoization of inbound and outbound
|
|
40
|
+
props. Do not use connectAdvanced directly without memoizing results between calls to your
|
|
41
|
+
selector, otherwise the Connect component will re-render on every state or props change.
|
|
42
|
+
*/
|
|
43
|
+
selectorFactory,
|
|
44
|
+
// options object:
|
|
45
|
+
{
|
|
46
|
+
// the func used to compute this HOC's displayName from the wrapped component's displayName.
|
|
47
|
+
// probably overridden by wrapper functions such as connect()
|
|
48
|
+
getDisplayName = (name) => `ConnectAdvanced(${name})`,
|
|
49
|
+
// shown in error messages
|
|
50
|
+
// probably overridden by wrapper functions such as connect()
|
|
51
|
+
methodName = 'connectAdvanced',
|
|
52
|
+
// stores to connect to
|
|
53
|
+
stores = [],
|
|
54
|
+
// determines whether this HOC subscribes to store changes
|
|
55
|
+
shouldHandleStateChanges = true,
|
|
56
|
+
// use React's forwardRef to expose a ref of the wrapped component
|
|
57
|
+
forwardRef = false,
|
|
58
|
+
// function used to schedule updates
|
|
59
|
+
schedule = (fn) => fn(),
|
|
60
|
+
// is component and all toProps functions are pure, so they can be memoized
|
|
61
|
+
pure = true,
|
|
62
|
+
// additional options are passed through to the selectorFactory
|
|
63
|
+
...connectOptions } = {}) {
|
|
64
|
+
return function wrapWithConnect(WrappedComponent) {
|
|
65
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
66
|
+
invariant(isValidElementType(WrappedComponent), `You must pass a component to the function returned by ` +
|
|
67
|
+
`${methodName}. Instead received ${stringifyComponent(WrappedComponent)}`);
|
|
68
|
+
}
|
|
69
|
+
const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
70
|
+
const displayName = getDisplayName(wrappedComponentName);
|
|
71
|
+
const selectorFactoryOptions = {
|
|
72
|
+
...connectOptions,
|
|
73
|
+
pure,
|
|
74
|
+
getDisplayName,
|
|
75
|
+
methodName,
|
|
76
|
+
shouldHandleStateChanges,
|
|
77
|
+
displayName,
|
|
78
|
+
wrappedComponentName,
|
|
79
|
+
WrappedComponent,
|
|
80
|
+
};
|
|
81
|
+
const ConnectFunction = (props) => {
|
|
82
|
+
const [forwardedRef, wrapperProps] = useMemo(() => {
|
|
83
|
+
// Distinguish between actual "data" props that were passed to the wrapper component,
|
|
84
|
+
// and values needed to control behavior (forwarded refs, alternate context instances).
|
|
85
|
+
// To maintain the wrapperProps object reference, memoize this destructuring.
|
|
86
|
+
// eslint-disable-next-line no-shadow
|
|
87
|
+
const { forwardedRef, ...wrapperProps } = props;
|
|
88
|
+
return [forwardedRef, wrapperProps];
|
|
89
|
+
}, [props]);
|
|
90
|
+
// Retrieve the store and ancestor subscription via context, if available
|
|
91
|
+
const contextValue = useConsumerContext();
|
|
92
|
+
const serverState = useContext(ServerStateContext);
|
|
93
|
+
invariant(Boolean(contextValue), `Could not find context in ` +
|
|
94
|
+
`"${displayName}". Either wrap the root component in a <Provider>, ` +
|
|
95
|
+
`or pass a custom React context provider to <Provider> and the corresponding ` +
|
|
96
|
+
`React context consumer to ${displayName} in connect options.`);
|
|
97
|
+
const childPropsSelector = useMemo(() => {
|
|
98
|
+
// The child props selector needs the store reference as an input.
|
|
99
|
+
// Re-create this selector whenever the store changes.
|
|
100
|
+
return selectorFactory(contextValue, selectorFactoryOptions);
|
|
101
|
+
}, [contextValue]);
|
|
102
|
+
const subscription = useMemo(() => {
|
|
103
|
+
if (!shouldHandleStateChanges)
|
|
104
|
+
return null;
|
|
105
|
+
return new Subscription(stores.map((store) => contextValue.getStore(store)));
|
|
106
|
+
}, [contextValue]);
|
|
107
|
+
useMemo(() => {
|
|
108
|
+
if (subscription && typeof window !== 'undefined') {
|
|
109
|
+
subscription.trySubscribe();
|
|
110
|
+
}
|
|
111
|
+
}, [subscription]);
|
|
112
|
+
const renderIsScheduled = useRef(false);
|
|
113
|
+
const actualChildPropsSelector = useCallback(() => {
|
|
114
|
+
return childPropsSelector(contextValue.getState(), wrapperProps);
|
|
115
|
+
}, [contextValue, wrapperProps, childPropsSelector]);
|
|
116
|
+
// Our re-subscribe logic only runs when the store/subscription setup changes
|
|
117
|
+
const subscribe = useCallback((reactUpdate) => {
|
|
118
|
+
if (!subscription) {
|
|
119
|
+
return noop;
|
|
120
|
+
}
|
|
121
|
+
subscription.setOnStateChange(() => {
|
|
122
|
+
if (!renderIsScheduled.current) {
|
|
123
|
+
renderIsScheduled.current = true;
|
|
124
|
+
schedule(() => {
|
|
125
|
+
reactUpdate();
|
|
126
|
+
renderIsScheduled.current = false;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return () => {
|
|
131
|
+
return subscription.tryUnsubscribe();
|
|
132
|
+
};
|
|
133
|
+
}, [subscription]);
|
|
134
|
+
const actualChildProps = useSyncExternalStore(subscribe, actualChildPropsSelector, serverState ? () => childPropsSelector(serverState, wrapperProps) : actualChildPropsSelector);
|
|
135
|
+
// Now that all that's done, we can finally try to actually render the child component.
|
|
136
|
+
// We memoize the elements for the rendered child component as an optimization.
|
|
137
|
+
const renderedWrappedComponent = useMemo(
|
|
138
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
139
|
+
() => jsx(WrappedComponent, { ...actualChildProps, ref: forwardedRef }), [forwardedRef, actualChildProps]);
|
|
140
|
+
return renderedWrappedComponent;
|
|
141
|
+
};
|
|
142
|
+
// If we're in "pure" mode, ensure our wrapper component only re-renders when incoming props have changed.
|
|
143
|
+
const Connect = pure ? React.memo(ConnectFunction) : ConnectFunction;
|
|
144
|
+
Connect.WrappedComponent = WrappedComponent;
|
|
145
|
+
Connect.displayName = displayName;
|
|
146
|
+
if (forwardRef) {
|
|
147
|
+
const forwarded = React.forwardRef(function forwardConnectRef(props, ref) {
|
|
148
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
149
|
+
return jsx(Connect, { ...props, forwardedRef: ref });
|
|
150
|
+
});
|
|
151
|
+
forwarded.displayName = displayName;
|
|
152
|
+
forwarded.WrappedComponent = WrappedComponent;
|
|
153
|
+
return hoistStatics(forwarded, WrappedComponent);
|
|
154
|
+
}
|
|
155
|
+
return hoistStatics(Connect, WrappedComponent);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export { connectAdvanced };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var noop = require('@tinkoff/utils/function/noop');
|
|
7
|
+
var hoistStatics = require('hoist-non-react-statics');
|
|
8
|
+
var invariant = require('invariant');
|
|
9
|
+
var React = require('react');
|
|
10
|
+
var reactIs = require('react-is');
|
|
11
|
+
var shim = require('use-sync-external-store/shim');
|
|
12
|
+
var Subscription = require('./Subscription.js');
|
|
13
|
+
var useConsumerContext = require('./hooks/useConsumerContext.js');
|
|
14
|
+
require('@tinkoff/utils/is/array');
|
|
15
|
+
require('@tinkoff/react-hooks');
|
|
16
|
+
var context = require('./context.js');
|
|
17
|
+
require('@tinkoff/utils/is/shallowEqual');
|
|
18
|
+
require('@tinkoff/utils/array/toArray');
|
|
19
|
+
require('use-sync-external-store/shim/with-selector');
|
|
20
|
+
|
|
21
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
22
|
+
|
|
23
|
+
var noop__default = /*#__PURE__*/_interopDefaultLegacy(noop);
|
|
24
|
+
var hoistStatics__default = /*#__PURE__*/_interopDefaultLegacy(hoistStatics);
|
|
25
|
+
var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
|
|
26
|
+
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
27
|
+
|
|
28
|
+
const stringifyComponent = (Comp) => {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(Comp);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
return String(Comp);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated
|
|
38
|
+
*/
|
|
39
|
+
function connectAdvanced(
|
|
40
|
+
/*
|
|
41
|
+
selectorFactory is a func that is responsible for returning the selector function used to
|
|
42
|
+
compute new props from state, props, and dispatch. For example:
|
|
43
|
+
export default connectAdvanced((dispatch, options) => (state, props) => ({
|
|
44
|
+
thing: state.things[props.thingId],
|
|
45
|
+
saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)),
|
|
46
|
+
}))(YourComponent)
|
|
47
|
+
Access to dispatch is provided to the factory so selectorFactories can bind actionCreators
|
|
48
|
+
outside of their selector as an optimization. Options passed to connectAdvanced are passed to
|
|
49
|
+
the selectorFactory, along with displayName and WrappedComponent, as the second argument.
|
|
50
|
+
Note that selectorFactory is responsible for all caching/memoization of inbound and outbound
|
|
51
|
+
props. Do not use connectAdvanced directly without memoizing results between calls to your
|
|
52
|
+
selector, otherwise the Connect component will re-render on every state or props change.
|
|
53
|
+
*/
|
|
54
|
+
selectorFactory,
|
|
55
|
+
// options object:
|
|
56
|
+
{
|
|
57
|
+
// the func used to compute this HOC's displayName from the wrapped component's displayName.
|
|
58
|
+
// probably overridden by wrapper functions such as connect()
|
|
59
|
+
getDisplayName = (name) => `ConnectAdvanced(${name})`,
|
|
60
|
+
// shown in error messages
|
|
61
|
+
// probably overridden by wrapper functions such as connect()
|
|
62
|
+
methodName = 'connectAdvanced',
|
|
63
|
+
// stores to connect to
|
|
64
|
+
stores = [],
|
|
65
|
+
// determines whether this HOC subscribes to store changes
|
|
66
|
+
shouldHandleStateChanges = true,
|
|
67
|
+
// use React's forwardRef to expose a ref of the wrapped component
|
|
68
|
+
forwardRef = false,
|
|
69
|
+
// function used to schedule updates
|
|
70
|
+
schedule = (fn) => fn(),
|
|
71
|
+
// is component and all toProps functions are pure, so they can be memoized
|
|
72
|
+
pure = true,
|
|
73
|
+
// additional options are passed through to the selectorFactory
|
|
74
|
+
...connectOptions } = {}) {
|
|
75
|
+
return function wrapWithConnect(WrappedComponent) {
|
|
76
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
77
|
+
invariant__default["default"](reactIs.isValidElementType(WrappedComponent), `You must pass a component to the function returned by ` +
|
|
78
|
+
`${methodName}. Instead received ${stringifyComponent(WrappedComponent)}`);
|
|
79
|
+
}
|
|
80
|
+
const wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
|
81
|
+
const displayName = getDisplayName(wrappedComponentName);
|
|
82
|
+
const selectorFactoryOptions = {
|
|
83
|
+
...connectOptions,
|
|
84
|
+
pure,
|
|
85
|
+
getDisplayName,
|
|
86
|
+
methodName,
|
|
87
|
+
shouldHandleStateChanges,
|
|
88
|
+
displayName,
|
|
89
|
+
wrappedComponentName,
|
|
90
|
+
WrappedComponent,
|
|
91
|
+
};
|
|
92
|
+
const ConnectFunction = (props) => {
|
|
93
|
+
const [forwardedRef, wrapperProps] = React.useMemo(() => {
|
|
94
|
+
// Distinguish between actual "data" props that were passed to the wrapper component,
|
|
95
|
+
// and values needed to control behavior (forwarded refs, alternate context instances).
|
|
96
|
+
// To maintain the wrapperProps object reference, memoize this destructuring.
|
|
97
|
+
// eslint-disable-next-line no-shadow
|
|
98
|
+
const { forwardedRef, ...wrapperProps } = props;
|
|
99
|
+
return [forwardedRef, wrapperProps];
|
|
100
|
+
}, [props]);
|
|
101
|
+
// Retrieve the store and ancestor subscription via context, if available
|
|
102
|
+
const contextValue = useConsumerContext.useConsumerContext();
|
|
103
|
+
const serverState = React.useContext(context.ServerStateContext);
|
|
104
|
+
invariant__default["default"](Boolean(contextValue), `Could not find context in ` +
|
|
105
|
+
`"${displayName}". Either wrap the root component in a <Provider>, ` +
|
|
106
|
+
`or pass a custom React context provider to <Provider> and the corresponding ` +
|
|
107
|
+
`React context consumer to ${displayName} in connect options.`);
|
|
108
|
+
const childPropsSelector = React.useMemo(() => {
|
|
109
|
+
// The child props selector needs the store reference as an input.
|
|
110
|
+
// Re-create this selector whenever the store changes.
|
|
111
|
+
return selectorFactory(contextValue, selectorFactoryOptions);
|
|
112
|
+
}, [contextValue]);
|
|
113
|
+
const subscription = React.useMemo(() => {
|
|
114
|
+
if (!shouldHandleStateChanges)
|
|
115
|
+
return null;
|
|
116
|
+
return new Subscription.Subscription(stores.map((store) => contextValue.getStore(store)));
|
|
117
|
+
}, [contextValue]);
|
|
118
|
+
React.useMemo(() => {
|
|
119
|
+
if (subscription && typeof window !== 'undefined') {
|
|
120
|
+
subscription.trySubscribe();
|
|
121
|
+
}
|
|
122
|
+
}, [subscription]);
|
|
123
|
+
const renderIsScheduled = React.useRef(false);
|
|
124
|
+
const actualChildPropsSelector = React.useCallback(() => {
|
|
125
|
+
return childPropsSelector(contextValue.getState(), wrapperProps);
|
|
126
|
+
}, [contextValue, wrapperProps, childPropsSelector]);
|
|
127
|
+
// Our re-subscribe logic only runs when the store/subscription setup changes
|
|
128
|
+
const subscribe = React.useCallback((reactUpdate) => {
|
|
129
|
+
if (!subscription) {
|
|
130
|
+
return noop__default["default"];
|
|
131
|
+
}
|
|
132
|
+
subscription.setOnStateChange(() => {
|
|
133
|
+
if (!renderIsScheduled.current) {
|
|
134
|
+
renderIsScheduled.current = true;
|
|
135
|
+
schedule(() => {
|
|
136
|
+
reactUpdate();
|
|
137
|
+
renderIsScheduled.current = false;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return () => {
|
|
142
|
+
return subscription.tryUnsubscribe();
|
|
143
|
+
};
|
|
144
|
+
}, [subscription]);
|
|
145
|
+
const actualChildProps = shim.useSyncExternalStore(subscribe, actualChildPropsSelector, serverState ? () => childPropsSelector(serverState, wrapperProps) : actualChildPropsSelector);
|
|
146
|
+
// Now that all that's done, we can finally try to actually render the child component.
|
|
147
|
+
// We memoize the elements for the rendered child component as an optimization.
|
|
148
|
+
const renderedWrappedComponent = React.useMemo(
|
|
149
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
150
|
+
() => jsxRuntime.jsx(WrappedComponent, { ...actualChildProps, ref: forwardedRef }), [forwardedRef, actualChildProps]);
|
|
151
|
+
return renderedWrappedComponent;
|
|
152
|
+
};
|
|
153
|
+
// If we're in "pure" mode, ensure our wrapper component only re-renders when incoming props have changed.
|
|
154
|
+
const Connect = pure ? React__default["default"].memo(ConnectFunction) : ConnectFunction;
|
|
155
|
+
Connect.WrappedComponent = WrappedComponent;
|
|
156
|
+
Connect.displayName = displayName;
|
|
157
|
+
if (forwardRef) {
|
|
158
|
+
const forwarded = React__default["default"].forwardRef(function forwardConnectRef(props, ref) {
|
|
159
|
+
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
160
|
+
return jsxRuntime.jsx(Connect, { ...props, forwardedRef: ref });
|
|
161
|
+
});
|
|
162
|
+
forwarded.displayName = displayName;
|
|
163
|
+
forwarded.WrappedComponent = WrappedComponent;
|
|
164
|
+
return hoistStatics__default["default"](forwarded, WrappedComponent);
|
|
165
|
+
}
|
|
166
|
+
return hoistStatics__default["default"](Connect, WrappedComponent);
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
exports.connectAdvanced = connectAdvanced;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
const ConnectContext = /* #__PURE__*/ React.createContext(null);
|
|
8
|
+
const ServerStateContext = /* #__PURE__*/ React.createContext(null);
|
|
9
|
+
|
|
10
|
+
exports.ConnectContext = ConnectContext;
|
|
11
|
+
exports.ServerStateContext = ServerStateContext;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import isArray from '@tinkoff/utils/is/array';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useShallowEqual } from '@tinkoff/react-hooks';
|
|
4
|
+
import { useConsumerContext } from './useConsumerContext.es.js';
|
|
5
|
+
|
|
6
|
+
function useActions(actions) {
|
|
7
|
+
const context = useConsumerContext();
|
|
8
|
+
const actionsRef = useShallowEqual(actions);
|
|
9
|
+
return useMemo(() => {
|
|
10
|
+
if (isArray(actionsRef)) {
|
|
11
|
+
return actionsRef.map((action) => context.executeAction.bind(context, action));
|
|
12
|
+
}
|
|
13
|
+
return context.executeAction.bind(context, actionsRef);
|
|
14
|
+
}, [actionsRef, context]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { useActions };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var isArray = require('@tinkoff/utils/is/array');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var reactHooks = require('@tinkoff/react-hooks');
|
|
8
|
+
var useConsumerContext = require('./useConsumerContext.js');
|
|
9
|
+
|
|
10
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
11
|
+
|
|
12
|
+
var isArray__default = /*#__PURE__*/_interopDefaultLegacy(isArray);
|
|
13
|
+
|
|
14
|
+
function useActions(actions) {
|
|
15
|
+
const context = useConsumerContext.useConsumerContext();
|
|
16
|
+
const actionsRef = reactHooks.useShallowEqual(actions);
|
|
17
|
+
return React.useMemo(() => {
|
|
18
|
+
if (isArray__default["default"](actionsRef)) {
|
|
19
|
+
return actionsRef.map((action) => context.executeAction.bind(context, action));
|
|
20
|
+
}
|
|
21
|
+
return context.executeAction.bind(context, actionsRef);
|
|
22
|
+
}, [actionsRef, context]);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.useActions = useActions;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { ConnectContext } from '../context.es.js';
|
|
3
|
+
|
|
4
|
+
const useConsumerContext = () => {
|
|
5
|
+
const context = useContext(ConnectContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error('CONSUMER_CONTEXT not found, have you added "@tramvai/module-common"?');
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { useConsumerContext };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React = require('react');
|
|
6
|
+
var context = require('../context.js');
|
|
7
|
+
|
|
8
|
+
const useConsumerContext = () => {
|
|
9
|
+
const context$1 = React.useContext(context.ConnectContext);
|
|
10
|
+
if (!context$1) {
|
|
11
|
+
throw new Error('CONSUMER_CONTEXT not found, have you added "@tramvai/module-common"?');
|
|
12
|
+
}
|
|
13
|
+
return context$1;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
exports.useConsumerContext = useConsumerContext;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import shallowEqual from '@tinkoff/utils/is/shallowEqual';
|
|
2
|
+
import { useContext, useRef, useMemo, useCallback } from 'react';
|
|
3
|
+
import { useShallowEqual, useIsomorphicLayoutEffect } from '@tinkoff/react-hooks';
|
|
4
|
+
import invariant from 'invariant';
|
|
5
|
+
import toArray from '@tinkoff/utils/array/toArray';
|
|
6
|
+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';
|
|
7
|
+
import { useConsumerContext } from './useConsumerContext.es.js';
|
|
8
|
+
import { Subscription } from '../Subscription.es.js';
|
|
9
|
+
import { scheduling } from '../scheduling.es.js';
|
|
10
|
+
import { ServerStateContext } from '../context.es.js';
|
|
11
|
+
|
|
12
|
+
const schedule = /* #__PURE__*/ scheduling();
|
|
13
|
+
function useSelector(storesOrStore, selector, equalityFn = shallowEqual) {
|
|
14
|
+
invariant(selector, `You must pass a selector to useSelectors`);
|
|
15
|
+
const context = useConsumerContext();
|
|
16
|
+
const serverState = useContext(ServerStateContext);
|
|
17
|
+
const renderIsScheduled = useRef(false);
|
|
18
|
+
const storesRef = useShallowEqual(storesOrStore);
|
|
19
|
+
const subscription = useMemo(() => new Subscription(toArray(storesRef).map(context.getStore)), [storesRef, context]);
|
|
20
|
+
const latestSubscriptionCallbackError = useRef();
|
|
21
|
+
const subscribe = useCallback((reactUpdate) => {
|
|
22
|
+
subscription.setOnStateChange(() => {
|
|
23
|
+
if (!renderIsScheduled.current) {
|
|
24
|
+
renderIsScheduled.current = true;
|
|
25
|
+
schedule(() => {
|
|
26
|
+
reactUpdate();
|
|
27
|
+
renderIsScheduled.current = false;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
subscription.trySubscribe();
|
|
32
|
+
return () => {
|
|
33
|
+
return subscription.tryUnsubscribe();
|
|
34
|
+
};
|
|
35
|
+
}, [subscription]);
|
|
36
|
+
let selectedState;
|
|
37
|
+
try {
|
|
38
|
+
selectedState = useSyncExternalStoreWithSelector(subscribe, context.getState, serverState ? () => serverState : context.getState, selector, equalityFn);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
let errorMessage = `An error occured while selecting the store state: ${err.message}.`;
|
|
42
|
+
if (latestSubscriptionCallbackError.current) {
|
|
43
|
+
errorMessage += `\nThe error may be correlated with this previous error:\n${latestSubscriptionCallbackError.current.stack}\n\nOriginal stack trace:`;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(errorMessage);
|
|
46
|
+
}
|
|
47
|
+
useIsomorphicLayoutEffect(() => {
|
|
48
|
+
latestSubscriptionCallbackError.current = undefined;
|
|
49
|
+
});
|
|
50
|
+
return selectedState;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { useSelector };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var shallowEqual = require('@tinkoff/utils/is/shallowEqual');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var reactHooks = require('@tinkoff/react-hooks');
|
|
8
|
+
var invariant = require('invariant');
|
|
9
|
+
var toArray = require('@tinkoff/utils/array/toArray');
|
|
10
|
+
var withSelector = require('use-sync-external-store/shim/with-selector');
|
|
11
|
+
var useConsumerContext = require('./useConsumerContext.js');
|
|
12
|
+
var Subscription = require('../Subscription.js');
|
|
13
|
+
var scheduling = require('../scheduling.js');
|
|
14
|
+
var context = require('../context.js');
|
|
15
|
+
|
|
16
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
17
|
+
|
|
18
|
+
var shallowEqual__default = /*#__PURE__*/_interopDefaultLegacy(shallowEqual);
|
|
19
|
+
var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
|
|
20
|
+
var toArray__default = /*#__PURE__*/_interopDefaultLegacy(toArray);
|
|
21
|
+
|
|
22
|
+
const schedule = /* #__PURE__*/ scheduling.scheduling();
|
|
23
|
+
function useSelector(storesOrStore, selector, equalityFn = shallowEqual__default["default"]) {
|
|
24
|
+
invariant__default["default"](selector, `You must pass a selector to useSelectors`);
|
|
25
|
+
const context$1 = useConsumerContext.useConsumerContext();
|
|
26
|
+
const serverState = React.useContext(context.ServerStateContext);
|
|
27
|
+
const renderIsScheduled = React.useRef(false);
|
|
28
|
+
const storesRef = reactHooks.useShallowEqual(storesOrStore);
|
|
29
|
+
const subscription = React.useMemo(() => new Subscription.Subscription(toArray__default["default"](storesRef).map(context$1.getStore)), [storesRef, context$1]);
|
|
30
|
+
const latestSubscriptionCallbackError = React.useRef();
|
|
31
|
+
const subscribe = React.useCallback((reactUpdate) => {
|
|
32
|
+
subscription.setOnStateChange(() => {
|
|
33
|
+
if (!renderIsScheduled.current) {
|
|
34
|
+
renderIsScheduled.current = true;
|
|
35
|
+
schedule(() => {
|
|
36
|
+
reactUpdate();
|
|
37
|
+
renderIsScheduled.current = false;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
subscription.trySubscribe();
|
|
42
|
+
return () => {
|
|
43
|
+
return subscription.tryUnsubscribe();
|
|
44
|
+
};
|
|
45
|
+
}, [subscription]);
|
|
46
|
+
let selectedState;
|
|
47
|
+
try {
|
|
48
|
+
selectedState = withSelector.useSyncExternalStoreWithSelector(subscribe, context$1.getState, serverState ? () => serverState : context$1.getState, selector, equalityFn);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
let errorMessage = `An error occured while selecting the store state: ${err.message}.`;
|
|
52
|
+
if (latestSubscriptionCallbackError.current) {
|
|
53
|
+
errorMessage += `\nThe error may be correlated with this previous error:\n${latestSubscriptionCallbackError.current.stack}\n\nOriginal stack trace:`;
|
|
54
|
+
}
|
|
55
|
+
throw new Error(errorMessage);
|
|
56
|
+
}
|
|
57
|
+
reactHooks.useIsomorphicLayoutEffect(() => {
|
|
58
|
+
latestSubscriptionCallbackError.current = undefined;
|
|
59
|
+
});
|
|
60
|
+
return selectedState;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
exports.useSelector = useSelector;
|