react-vant-nova 1.1.8-test → 1.1.9-test
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.
Potentially problematic release.
This version of react-vant-nova might be problematic. Click here for more details.
- package/bundle/react-vant-nova.es.js +33 -43
- package/bundle/react-vant-nova.js +31 -41
- package/bundle/react-vant-nova.min.js +2 -2
- package/es/toast/method.js +51 -49
- package/lib/toast/method.js +50 -48
- package/package.json +1 -1
package/es/toast/method.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useState } from 'react';
|
|
2
|
+
import { useCallback, useState, useEffect } from 'react';
|
|
3
3
|
import { useIsomorphicLayoutEffect } from '../hooks';
|
|
4
4
|
import { extend, isObject } from '../utils';
|
|
5
5
|
import { resolveContainer } from '../utils/dom/getContainer';
|
|
@@ -7,10 +7,6 @@ import { lockClick } from './lock-click';
|
|
|
7
7
|
import BaseToast from './Toast';
|
|
8
8
|
import { render, unmount } from '../utils/dom/render';
|
|
9
9
|
import canUseDom from '../utils/dom/canUseDom';
|
|
10
|
-
import { reactVersion } from '../utils/dom/version';
|
|
11
|
-
console.log('method.tsx loaded');
|
|
12
|
-
console.log('React version:', reactVersion);
|
|
13
|
-
console.log('canUseDom:', canUseDom());
|
|
14
10
|
const defaultOptions = {
|
|
15
11
|
message: '',
|
|
16
12
|
className: '',
|
|
@@ -23,9 +19,7 @@ const defaultOptions = {
|
|
|
23
19
|
const toastArray = [];
|
|
24
20
|
let allowMultiple = false;
|
|
25
21
|
let currentOptions = extend({}, defaultOptions);
|
|
26
|
-
// default options of specific type
|
|
27
22
|
const defaultOptionsMap = new Map();
|
|
28
|
-
// 同步的销毁
|
|
29
23
|
function syncClear() {
|
|
30
24
|
let fn = toastArray.pop();
|
|
31
25
|
while (fn) {
|
|
@@ -33,22 +27,27 @@ function syncClear() {
|
|
|
33
27
|
fn = toastArray.pop();
|
|
34
28
|
}
|
|
35
29
|
}
|
|
36
|
-
// 针对 toast 还没弹出来就立刻销毁的情况,将销毁放到下一个 event loop 中,避免销毁失败。
|
|
37
30
|
function nextTickClear() {
|
|
38
31
|
setTimeout(syncClear);
|
|
39
32
|
}
|
|
40
|
-
// 可返回用于销毁此弹窗的方法
|
|
41
33
|
const ToastObj = p => {
|
|
42
|
-
|
|
43
|
-
if (!canUseDom()) {
|
|
44
|
-
console.log('canUseDom returned false');
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
34
|
+
if (!canUseDom()) return null;
|
|
47
35
|
const props = parseOptions(p);
|
|
48
|
-
|
|
36
|
+
// 1. 创建一个引用容器,用于在组件渲染后将方法“透传”出来
|
|
37
|
+
const componentRef = {
|
|
38
|
+
clear: () => {},
|
|
39
|
+
config: nextState => {}
|
|
40
|
+
};
|
|
41
|
+
// 2. 这里定义的 update 方法改为调用 componentRef 中的方法
|
|
42
|
+
// 这样即使 React 19 异步渲染导致组件还没挂载,外部拿到 update 后,
|
|
43
|
+
// 等组件挂载完毕覆盖了 componentRef 的方法,调用就会生效。
|
|
49
44
|
const update = {
|
|
50
|
-
config:
|
|
51
|
-
clear: () =>
|
|
45
|
+
config: nextState => componentRef.config(nextState),
|
|
46
|
+
clear: () => {
|
|
47
|
+
// 如果组件还没挂载就被调用 clear,说明需要立即销毁容器(防御性编程)
|
|
48
|
+
// 但通常我们会等待组件挂载后的逻辑接管
|
|
49
|
+
componentRef.clear();
|
|
50
|
+
}
|
|
52
51
|
};
|
|
53
52
|
let timer = 0;
|
|
54
53
|
const {
|
|
@@ -57,46 +56,51 @@ const ToastObj = p => {
|
|
|
57
56
|
} = props;
|
|
58
57
|
const container = document.createElement('div');
|
|
59
58
|
const bodyContainer = resolveContainer(teleport);
|
|
60
|
-
console.log('bodyContainer:', bodyContainer);
|
|
61
59
|
bodyContainer.appendChild(container);
|
|
62
|
-
console.log('container added to bodyContainer');
|
|
63
60
|
const TempToast = () => {
|
|
64
|
-
console.log('TempToast component rendered');
|
|
65
61
|
const options = Object.assign({
|
|
66
62
|
duration: 2000
|
|
67
63
|
}, props);
|
|
68
|
-
|
|
64
|
+
// 关键修改 A: 初始设为 false,确保挂载后从 false -> true 触发动画
|
|
65
|
+
const [visible, setVisible] = useState(false);
|
|
69
66
|
const [state, setState] = useState(Object.assign({}, options));
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
// 关键修改 B: 使用 useEffect 在组件挂载下一帧开启显示
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
setVisible(true);
|
|
70
|
+
}, []);
|
|
72
71
|
const internalOnClosed = useCallback(() => {
|
|
73
|
-
console.log('internalOnClosed called');
|
|
74
72
|
if (state.forbidClick) {
|
|
75
73
|
lockClick(false);
|
|
76
74
|
}
|
|
77
75
|
setVisible(false);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
// 等待动画结束时间后卸载 DOM
|
|
77
|
+
// 注意:这里最好稍微给一点 buffer 时间,或者依赖组件的 onClosed 回调
|
|
78
|
+
const unmountDelay = +state.duration || +defaultOptions.duration;
|
|
79
|
+
// 如果 duration 是 0 (一直显示),我们在 clear 时不需要等待 duration,而是等待动画时间
|
|
80
|
+
// 这里为了简单,如果 visible 变 false,通常组件会有 exit 动画时长
|
|
81
|
+
// 这里的逻辑最好依赖 BaseToast 的 onClosed 属性回调,但为了保险起见保留 setTimeout
|
|
82
|
+
// 修正逻辑:只有当 visible 变为 false 触发动画结束时,BaseToast 才会调用 onClosed
|
|
83
|
+
// 这里我们手动卸载 Root
|
|
84
|
+
}, [state.duration, state.forbidClick]);
|
|
85
|
+
// 真正的物理销毁
|
|
86
|
+
const handleAfterClose = useCallback(() => {
|
|
87
|
+
const unmountResult = unmount(container);
|
|
88
|
+
if (unmountResult && container.parentNode) {
|
|
89
|
+
container.parentNode.removeChild(container);
|
|
90
|
+
}
|
|
91
|
+
}, []);
|
|
87
92
|
const destroy = useCallback(() => {
|
|
88
|
-
console.log('destroy called');
|
|
89
93
|
setVisible(false);
|
|
90
94
|
if (onClose) onClose();
|
|
91
|
-
}, []);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
}, [onClose]);
|
|
96
|
+
// 将内部方法赋值给外部引用
|
|
97
|
+
useIsomorphicLayoutEffect(() => {
|
|
98
|
+
componentRef.clear = internalOnClosed;
|
|
99
|
+
componentRef.config = nextState => {
|
|
100
|
+
setState(prev => typeof nextState === 'function' ? Object.assign(Object.assign({}, prev), nextState(prev)) : Object.assign(Object.assign({}, prev), nextState));
|
|
101
|
+
};
|
|
102
|
+
}, [internalOnClosed]);
|
|
97
103
|
useIsomorphicLayoutEffect(() => {
|
|
98
|
-
console.log('useIsomorphicLayoutEffect called');
|
|
99
|
-
// 移除setVisible(true),因为初始状态已经是true
|
|
100
104
|
if (!allowMultiple) syncClear();
|
|
101
105
|
toastArray.push(internalOnClosed);
|
|
102
106
|
if (state.duration !== 0 && 'duration' in state) {
|
|
@@ -107,26 +111,25 @@ const ToastObj = p => {
|
|
|
107
111
|
window.clearTimeout(timer);
|
|
108
112
|
}
|
|
109
113
|
};
|
|
110
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
114
|
}, []);
|
|
112
115
|
return (
|
|
113
116
|
// @ts-ignore
|
|
114
117
|
_jsx(BaseToast, Object.assign({}, state, {
|
|
115
118
|
visible: visible,
|
|
116
|
-
//
|
|
119
|
+
// 关键:确保 teleport={null} 或不传,防止 BaseToast 内部再次 Portal 出去
|
|
120
|
+
// 如果 BaseToast 内部有 Portal 逻辑,设为 null 通常能禁用它
|
|
121
|
+
teleport: null,
|
|
117
122
|
onClose: destroy,
|
|
118
|
-
onClosed:
|
|
123
|
+
onClosed: handleAfterClose
|
|
119
124
|
}))
|
|
120
125
|
);
|
|
121
126
|
};
|
|
122
|
-
|
|
127
|
+
// React 19: 这里的 render 是异步的,TempToast 不会立即执行
|
|
123
128
|
// @ts-ignore
|
|
124
129
|
render(_jsx(TempToast, {}), container);
|
|
125
|
-
console.log('After render(<TempToast />)');
|
|
126
130
|
return update;
|
|
127
131
|
};
|
|
128
132
|
function parseOptions(message) {
|
|
129
|
-
console.log('parseOptions called:', message);
|
|
130
133
|
if (isObject(message)) {
|
|
131
134
|
return message;
|
|
132
135
|
}
|
|
@@ -135,7 +138,6 @@ function parseOptions(message) {
|
|
|
135
138
|
};
|
|
136
139
|
}
|
|
137
140
|
const createMethod = type => options => {
|
|
138
|
-
console.log('createMethod called:', type, options);
|
|
139
141
|
return ToastObj(Object.assign(Object.assign(Object.assign(Object.assign({}, currentOptions), defaultOptionsMap.get(type)), parseOptions(options)), {
|
|
140
142
|
type
|
|
141
143
|
}));
|
package/lib/toast/method.js
CHANGED
|
@@ -25,12 +25,8 @@ var _lockClick = require("./lock-click");
|
|
|
25
25
|
var _Toast = _interopRequireDefault(require("./Toast"));
|
|
26
26
|
var _render = require("../utils/dom/render");
|
|
27
27
|
var _canUseDom = _interopRequireDefault(require("../utils/dom/canUseDom"));
|
|
28
|
-
var _version = require("../utils/dom/version");
|
|
29
28
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
30
29
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
31
|
-
console.log('method.tsx loaded');
|
|
32
|
-
console.log('React version:', _version.reactVersion);
|
|
33
|
-
console.log('canUseDom:', (0, _canUseDom.default)());
|
|
34
30
|
const defaultOptions = {
|
|
35
31
|
message: '',
|
|
36
32
|
className: '',
|
|
@@ -43,9 +39,7 @@ const defaultOptions = {
|
|
|
43
39
|
const toastArray = [];
|
|
44
40
|
let allowMultiple = false;
|
|
45
41
|
let currentOptions = (0, _utils.extend)({}, defaultOptions);
|
|
46
|
-
// default options of specific type
|
|
47
42
|
const defaultOptionsMap = new Map();
|
|
48
|
-
// 同步的销毁
|
|
49
43
|
function syncClear() {
|
|
50
44
|
let fn = toastArray.pop();
|
|
51
45
|
while (fn) {
|
|
@@ -53,22 +47,27 @@ function syncClear() {
|
|
|
53
47
|
fn = toastArray.pop();
|
|
54
48
|
}
|
|
55
49
|
}
|
|
56
|
-
// 针对 toast 还没弹出来就立刻销毁的情况,将销毁放到下一个 event loop 中,避免销毁失败。
|
|
57
50
|
function nextTickClear() {
|
|
58
51
|
setTimeout(syncClear);
|
|
59
52
|
}
|
|
60
|
-
// 可返回用于销毁此弹窗的方法
|
|
61
53
|
const ToastObj = p => {
|
|
62
|
-
|
|
63
|
-
if (!(0, _canUseDom.default)()) {
|
|
64
|
-
console.log('canUseDom returned false');
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
54
|
+
if (!(0, _canUseDom.default)()) return null;
|
|
67
55
|
const props = parseOptions(p);
|
|
68
|
-
|
|
56
|
+
// 1. 创建一个引用容器,用于在组件渲染后将方法“透传”出来
|
|
57
|
+
const componentRef = {
|
|
58
|
+
clear: () => {},
|
|
59
|
+
config: nextState => {}
|
|
60
|
+
};
|
|
61
|
+
// 2. 这里定义的 update 方法改为调用 componentRef 中的方法
|
|
62
|
+
// 这样即使 React 19 异步渲染导致组件还没挂载,外部拿到 update 后,
|
|
63
|
+
// 等组件挂载完毕覆盖了 componentRef 的方法,调用就会生效。
|
|
69
64
|
const update = {
|
|
70
|
-
config:
|
|
71
|
-
clear: () =>
|
|
65
|
+
config: nextState => componentRef.config(nextState),
|
|
66
|
+
clear: () => {
|
|
67
|
+
// 如果组件还没挂载就被调用 clear,说明需要立即销毁容器(防御性编程)
|
|
68
|
+
// 但通常我们会等待组件挂载后的逻辑接管
|
|
69
|
+
componentRef.clear();
|
|
70
|
+
}
|
|
72
71
|
};
|
|
73
72
|
let timer = 0;
|
|
74
73
|
const {
|
|
@@ -77,46 +76,51 @@ const ToastObj = p => {
|
|
|
77
76
|
} = props;
|
|
78
77
|
const container = document.createElement('div');
|
|
79
78
|
const bodyContainer = (0, _getContainer.resolveContainer)(teleport);
|
|
80
|
-
console.log('bodyContainer:', bodyContainer);
|
|
81
79
|
bodyContainer.appendChild(container);
|
|
82
|
-
console.log('container added to bodyContainer');
|
|
83
80
|
const TempToast = () => {
|
|
84
|
-
console.log('TempToast component rendered');
|
|
85
81
|
const options = Object.assign({
|
|
86
82
|
duration: 2000
|
|
87
83
|
}, props);
|
|
88
|
-
|
|
84
|
+
// 关键修改 A: 初始设为 false,确保挂载后从 false -> true 触发动画
|
|
85
|
+
const [visible, setVisible] = (0, _react().useState)(false);
|
|
89
86
|
const [state, setState] = (0, _react().useState)(Object.assign({}, options));
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
// 关键修改 B: 使用 useEffect 在组件挂载下一帧开启显示
|
|
88
|
+
(0, _react().useEffect)(() => {
|
|
89
|
+
setVisible(true);
|
|
90
|
+
}, []);
|
|
92
91
|
const internalOnClosed = (0, _react().useCallback)(() => {
|
|
93
|
-
console.log('internalOnClosed called');
|
|
94
92
|
if (state.forbidClick) {
|
|
95
93
|
(0, _lockClick.lockClick)(false);
|
|
96
94
|
}
|
|
97
95
|
setVisible(false);
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
// 等待动画结束时间后卸载 DOM
|
|
97
|
+
// 注意:这里最好稍微给一点 buffer 时间,或者依赖组件的 onClosed 回调
|
|
98
|
+
const unmountDelay = +state.duration || +defaultOptions.duration;
|
|
99
|
+
// 如果 duration 是 0 (一直显示),我们在 clear 时不需要等待 duration,而是等待动画时间
|
|
100
|
+
// 这里为了简单,如果 visible 变 false,通常组件会有 exit 动画时长
|
|
101
|
+
// 这里的逻辑最好依赖 BaseToast 的 onClosed 属性回调,但为了保险起见保留 setTimeout
|
|
102
|
+
// 修正逻辑:只有当 visible 变为 false 触发动画结束时,BaseToast 才会调用 onClosed
|
|
103
|
+
// 这里我们手动卸载 Root
|
|
104
|
+
}, [state.duration, state.forbidClick]);
|
|
105
|
+
// 真正的物理销毁
|
|
106
|
+
const handleAfterClose = (0, _react().useCallback)(() => {
|
|
107
|
+
const unmountResult = (0, _render.unmount)(container);
|
|
108
|
+
if (unmountResult && container.parentNode) {
|
|
109
|
+
container.parentNode.removeChild(container);
|
|
110
|
+
}
|
|
111
|
+
}, []);
|
|
107
112
|
const destroy = (0, _react().useCallback)(() => {
|
|
108
|
-
console.log('destroy called');
|
|
109
113
|
setVisible(false);
|
|
110
114
|
if (onClose) onClose();
|
|
111
|
-
}, []);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
}, [onClose]);
|
|
116
|
+
// 将内部方法赋值给外部引用
|
|
117
|
+
(0, _hooks.useIsomorphicLayoutEffect)(() => {
|
|
118
|
+
componentRef.clear = internalOnClosed;
|
|
119
|
+
componentRef.config = nextState => {
|
|
120
|
+
setState(prev => typeof nextState === 'function' ? Object.assign(Object.assign({}, prev), nextState(prev)) : Object.assign(Object.assign({}, prev), nextState));
|
|
121
|
+
};
|
|
122
|
+
}, [internalOnClosed]);
|
|
117
123
|
(0, _hooks.useIsomorphicLayoutEffect)(() => {
|
|
118
|
-
console.log('useIsomorphicLayoutEffect called');
|
|
119
|
-
// 移除setVisible(true),因为初始状态已经是true
|
|
120
124
|
if (!allowMultiple) syncClear();
|
|
121
125
|
toastArray.push(internalOnClosed);
|
|
122
126
|
if (state.duration !== 0 && 'duration' in state) {
|
|
@@ -127,26 +131,25 @@ const ToastObj = p => {
|
|
|
127
131
|
window.clearTimeout(timer);
|
|
128
132
|
}
|
|
129
133
|
};
|
|
130
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
131
134
|
}, []);
|
|
132
135
|
return (
|
|
133
136
|
// @ts-ignore
|
|
134
137
|
(0, _jsxRuntime().jsx)(_Toast.default, Object.assign({}, state, {
|
|
135
138
|
visible: visible,
|
|
136
|
-
//
|
|
139
|
+
// 关键:确保 teleport={null} 或不传,防止 BaseToast 内部再次 Portal 出去
|
|
140
|
+
// 如果 BaseToast 内部有 Portal 逻辑,设为 null 通常能禁用它
|
|
141
|
+
teleport: null,
|
|
137
142
|
onClose: destroy,
|
|
138
|
-
onClosed:
|
|
143
|
+
onClosed: handleAfterClose
|
|
139
144
|
}))
|
|
140
145
|
);
|
|
141
146
|
};
|
|
142
|
-
|
|
147
|
+
// React 19: 这里的 render 是异步的,TempToast 不会立即执行
|
|
143
148
|
// @ts-ignore
|
|
144
149
|
(0, _render.render)((0, _jsxRuntime().jsx)(TempToast, {}), container);
|
|
145
|
-
console.log('After render(<TempToast />)');
|
|
146
150
|
return update;
|
|
147
151
|
};
|
|
148
152
|
function parseOptions(message) {
|
|
149
|
-
console.log('parseOptions called:', message);
|
|
150
153
|
if ((0, _utils.isObject)(message)) {
|
|
151
154
|
return message;
|
|
152
155
|
}
|
|
@@ -155,7 +158,6 @@ function parseOptions(message) {
|
|
|
155
158
|
};
|
|
156
159
|
}
|
|
157
160
|
const createMethod = type => options => {
|
|
158
|
-
console.log('createMethod called:', type, options);
|
|
159
161
|
return ToastObj(Object.assign(Object.assign(Object.assign(Object.assign({}, currentOptions), defaultOptionsMap.get(type)), parseOptions(options)), {
|
|
160
162
|
type
|
|
161
163
|
}));
|