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.

@@ -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
- console.log('ToastObj called');
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
- console.log('props:', props);
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: () => null
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
- const [visible, setVisible] = useState(true); // 初始化为true,确保组件渲染时可见
64
+ // 关键修改 A: 初始设为 false,确保挂载后从 false -> true 触发动画
65
+ const [visible, setVisible] = useState(false);
69
66
  const [state, setState] = useState(Object.assign({}, options));
70
- console.log('TempToast useState initialized, visible:', visible);
71
- // clearDOM after animation
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
- window.setTimeout(() => {
79
- const unmountResult = unmount(container);
80
- if (unmountResult && container.parentNode) {
81
- container.parentNode.removeChild(container);
82
- }
83
- }, +state.duration || +defaultOptions.duration);
84
- // eslint-disable-next-line react-hooks/exhaustive-deps
85
- }, [container]);
86
- // close with animation
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
- update.clear = internalOnClosed;
93
- update.config = useCallback(nextState => {
94
- console.log('update.config called:', nextState);
95
- setState(prev => typeof nextState === 'function' ? Object.assign(Object.assign({}, prev), nextState(prev)) : Object.assign(Object.assign({}, prev), nextState));
96
- }, [setState]);
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
- // 移除teleport属性,避免嵌套的createPortal调用
119
+ // 关键:确保 teleport={null} 或不传,防止 BaseToast 内部再次 Portal 出去
120
+ // 如果 BaseToast 内部有 Portal 逻辑,设为 null 通常能禁用它
121
+ teleport: null,
117
122
  onClose: destroy,
118
- onClosed: internalOnClosed
123
+ onClosed: handleAfterClose
119
124
  }))
120
125
  );
121
126
  };
122
- console.log('Before render(<TempToast />)');
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
  }));
@@ -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
- console.log('ToastObj called');
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
- console.log('props:', props);
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: () => null
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
- const [visible, setVisible] = (0, _react().useState)(true); // 初始化为true,确保组件渲染时可见
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
- console.log('TempToast useState initialized, visible:', visible);
91
- // clearDOM after animation
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
- window.setTimeout(() => {
99
- const unmountResult = (0, _render.unmount)(container);
100
- if (unmountResult && container.parentNode) {
101
- container.parentNode.removeChild(container);
102
- }
103
- }, +state.duration || +defaultOptions.duration);
104
- // eslint-disable-next-line react-hooks/exhaustive-deps
105
- }, [container]);
106
- // close with animation
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
- update.clear = internalOnClosed;
113
- update.config = (0, _react().useCallback)(nextState => {
114
- console.log('update.config called:', nextState);
115
- setState(prev => typeof nextState === 'function' ? Object.assign(Object.assign({}, prev), nextState(prev)) : Object.assign(Object.assign({}, prev), nextState));
116
- }, [setState]);
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
- // 移除teleport属性,避免嵌套的createPortal调用
139
+ // 关键:确保 teleport={null} 或不传,防止 BaseToast 内部再次 Portal 出去
140
+ // 如果 BaseToast 内部有 Portal 逻辑,设为 null 通常能禁用它
141
+ teleport: null,
137
142
  onClose: destroy,
138
- onClosed: internalOnClosed
143
+ onClosed: handleAfterClose
139
144
  }))
140
145
  );
141
146
  };
142
- console.log('Before render(<TempToast />)');
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
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-vant-nova",
3
- "version": "1.1.8-test",
3
+ "version": "1.1.9-test",
4
4
  "description": "React Mobile UI Components based on Vant UI (兼容 React 19+,新增轻量 Table 组件)",
5
5
  "keywords": [
6
6
  "ui",