Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swiping the chart cause InteractionManager.runAfterInteractions not called #81

Closed
ekoooo opened this issue Jul 10, 2023 · 11 comments · Fixed by #83
Closed

swiping the chart cause InteractionManager.runAfterInteractions not called #81

ekoooo opened this issue Jul 10, 2023 · 11 comments · Fixed by #83
Assignees

Comments

@ekoooo
Copy link

ekoooo commented Jul 10, 2023

Describe the bug
滑动图表导致 InteractionManager.runAfterInteractions 回调未调用

To Reproduce
Steps to reproduce the behavior:

  1. 打开 /node_modules/react-native/Libraries/Interaction/InteractionManager.js 修改 DEBUG 变量值为 const DEBUG: true = true; 为了打开 InteractionManager 日志输出;
  2. 使用SVGRenderer 创建一个 PieChart 图表;
  3. 单指滑动图表;
  4. 查看 log, 只见 InteractionManager: create interaction handle, 并未见 InteractionManager: clear interaction handle.
  5. 由于 InteractionManager 未清除 interaction handle, 所以导致之后所有 InteractionManager.runAfterInteractions 都不会执行回调;

Expected behavior
InteractionManager.runAfterInteractions 回调正常调用

Screenshots
react-native-echarts/src/components/PanResponderHandler.tsx

image

Desktop (please complete the following information):
yarn react-native info

System:
    OS: macOS 12.5
    CPU: (10) x64 Apple M1 Pro
    Memory: 27.81 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 10.13.0 - /var/folders/jf/cxqjd8792n56l9qb0hd1x9zm0000gn/T/yarn--1688960306407-0.10676307975997412/node
    Yarn: 1.22.19 - /var/folders/jf/cxqjd8792n56l9qb0hd1x9zm0000gn/T/yarn--1688960306407-0.10676307975997412/yarn
    npm: 6.4.1 - ~/.nvm/versions/node/v10.13.0/bin/npm
    Watchman: 2023.05.22.00 - /opt/homebrew/bin/watchman
  Managers:
    CocoaPods: 1.11.3 - /opt/homebrew/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.1, iOS 16.1, macOS 13.0, tvOS 16.1, watchOS 9.1
    Android SDK:
      Android NDK: 20.1.5948944
  IDEs:
    Android Studio: 2022.2 AI-222.4459.24.2221.9862592
    Xcode: 14.1/14B47b - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.18 - /Users/lwl/.sdkman/candidates/java/current/bin/javac
    Python: 3.7.9 - /Users/lwl/.pyenv/shims/python
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.11.0 => 16.11.0
    react-native: 0.62.3 => 0.62.3
  npmGlobalPackages:
    *react-native*: Not Found

Smartphone (please complete the following information):

== Devices ==
-- iOS 16.1 --
    iPhone 11 (F9A8C42D-B78E-4F38-B47A-6A1C13D969A6) (Booted)

Additional context
"@wuba/react-native-echarts": "^1.2.1"

@ekoooo
Copy link
Author

ekoooo commented Jul 10, 2023

由于我使用图表只用于显示作用,直接用透明 View 遮住图表先暂时解决这个问题。

@zhiqingchen
Copy link
Member

pie图单指滑动就会有这个错误吗?还是需要来开debug才会有?

@zhiqingchen
Copy link
Member

修改后能运行的地方是?

@ekoooo
Copy link
Author

ekoooo commented Jul 10, 2023

pie图单指滑动就会有这个错误吗?还是需要来开debug才会有?

只要是滑动就会有这个问题,不一定要开 debug, 我修改 /node_modules/react-native/Libraries/Interaction/InteractionManager.js 这里的 debug 标志是为了能看清楚 log

@ekoooo
Copy link
Author

ekoooo commented Jul 10, 2023

修改后能运行的地方是?

修改了这个文件的代码

export function usePanResponder(
dispatchEvents: DispatchEvents
): [PanResponderInstance] {
const [zooming, setZooming] = useState(false);
const [moving, setMoving] = useState(false);
const pan = useRef({
initialX: 0,
initialY: 0,
prevDistance: 0,
});
const panResponder = useMemo(
() =>
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: ({ nativeEvent }) => {
dispatchEvents(['mousedown', 'mousemove'], nativeEvent);
},
onPanResponderMove: ({ nativeEvent }) => {
const touches = nativeEvent.touches;
const length = touches.length;
if (length === 1) {
if (!moving || zooming) {
setMoving(true);
setZooming(false);
} else {
dispatchEvents(['mousemove'], nativeEvent);
}
} else if (length === 2) {
const [
{ locationX: x0, locationY: y0 },
{ locationX: x1, locationY: y1 },
] = touches as [NativeTouchEvent, NativeTouchEvent];
const distance = calcDistance(x0, y0, x1, y1);
const { x, y } = calcCenter(x0, y0, x1, y1);
if (!zooming) {
pan.current = {
initialX: x,
initialY: y,
prevDistance: distance,
};
setZooming(true);
} else {
const { initialX, initialY, prevDistance } = pan.current;
const delta = distance - prevDistance;
pan.current.prevDistance = distance;
dispatchEvents(['mousewheel'], nativeEvent, {
zrX: initialX,
zrY: initialY,
zrDelta: delta / 120,
});
}
}
},
onPanResponderTerminationRequest: () => true,
onPanResponderRelease: ({ nativeEvent }) => {
if (!zooming) {
dispatchEvents(['mouseup', 'click'], nativeEvent);
}
setMoving(false);
setZooming(false);
},
onPanResponderTerminate: () => {},
onShouldBlockNativeResponder: () => {
return false;
},
}),
[dispatchEvents, moving, zooming, pan]
);
return [panResponder];
}

把这里

const panResponder = useMemo(

改成了下面的这样(只是简单的把 useMemo 改成了 useRef),就能正常的 InteractionManager: clear interaction handle

const panResponder = useRef(PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    onStartShouldSetPanResponderCapture: () => true,
    onMoveShouldSetPanResponder: () => true,
    onMoveShouldSetPanResponderCapture: () => true,
    onPanResponderGrant: _ref2 => {
      let {
        nativeEvent
      } = _ref2;
      dispatchEvents(['mousedown', 'mousemove'], nativeEvent);
    },
    onPanResponderMove: _ref3 => {
      let {
        nativeEvent
      } = _ref3;
      const touches = nativeEvent.touches;
      const length = touches.length;
      if (length === 1) {
        if (!moving || zooming) {
          setMoving(true);
          setZooming(false);
        } else {
          dispatchEvents(['mousemove'], nativeEvent);
        }
      } else if (length === 2) {
        const [{
          locationX: x0,
          locationY: y0
        }, {
          locationX: x1,
          locationY: y1
        }] = touches;
        const distance = calcDistance(x0, y0, x1, y1);
        const {
          x,
          y
        } = calcCenter(x0, y0, x1, y1);
        if (!zooming) {
          pan.current = {
            initialX: x,
            initialY: y,
            prevDistance: distance
          };
          setZooming(true);
        } else {
          const {
            initialX,
            initialY,
            prevDistance
          } = pan.current;
          const delta = distance - prevDistance;
          pan.current.prevDistance = distance;
          dispatchEvents(['mousewheel'], nativeEvent, {
            zrX: initialX,
            zrY: initialY,
            zrDelta: delta / 120
          });
        }
      }
    },
    onPanResponderTerminationRequest: () => true,
    onPanResponderRelease: _ref4 => {
      let {
        nativeEvent
      } = _ref4;
      if (!zooming) {
        dispatchEvents(['mouseup', 'click'], nativeEvent);
      }
      setMoving(false);
      setZooming(false);
    },
    onPanResponderTerminate: () => {},
    onShouldBlockNativeResponder: () => {
      return false;
    }
  })).current;

@zhiqingchen
Copy link
Member

handleGesture={false}可禁用手势

@zhiqingchen
Copy link
Member

@zhiqingchen zhiqingchen self-assigned this Jul 11, 2023
@zhiqingchen
Copy link
Member

try @wuba/react-native-echarts@1.2.2-alpha.0
@ekoooo

@ekoooo
Copy link
Author

ekoooo commented Jul 11, 2023

@zhiqingchen 你好,经过这边调试,确系是下面这个函数的问题

export function usePanResponder(
dispatchEvents: DispatchEvents
): [PanResponderInstance] {
const [zooming, setZooming] = useState(false);
const [moving, setMoving] = useState(false);
const pan = useRef({
initialX: 0,
initialY: 0,
prevDistance: 0,
});
const panResponder = useMemo(
() =>
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: ({ nativeEvent }) => {
dispatchEvents(['mousedown', 'mousemove'], nativeEvent);
},
onPanResponderMove: ({ nativeEvent }) => {
const touches = nativeEvent.touches;
const length = touches.length;
if (length === 1) {
if (!moving || zooming) {
setMoving(true);
setZooming(false);
} else {
dispatchEvents(['mousemove'], nativeEvent);
}
} else if (length === 2) {
const [
{ locationX: x0, locationY: y0 },
{ locationX: x1, locationY: y1 },
] = touches as [NativeTouchEvent, NativeTouchEvent];
const distance = calcDistance(x0, y0, x1, y1);
const { x, y } = calcCenter(x0, y0, x1, y1);
if (!zooming) {
pan.current = {
initialX: x,
initialY: y,
prevDistance: distance,
};
setZooming(true);
} else {
const { initialX, initialY, prevDistance } = pan.current;
const delta = distance - prevDistance;
pan.current.prevDistance = distance;
dispatchEvents(['mousewheel'], nativeEvent, {
zrX: initialX,
zrY: initialY,
zrDelta: delta / 120,
});
}
}
},
onPanResponderTerminationRequest: () => true,
onPanResponderRelease: ({ nativeEvent }) => {
if (!zooming) {
dispatchEvents(['mouseup', 'click'], nativeEvent);
}
setMoving(false);
setZooming(false);
},
onPanResponderTerminate: () => {},
onShouldBlockNativeResponder: () => {
return false;
},
}),
[dispatchEvents, moving, zooming, pan]
);
return [panResponder];
}

由于创建 panResponder 使用 useMemo 依赖了 moving, zooming, 从按下到滑动过程中 moving 的值发生变更,导致 PanResponder.create 重新创建,由于原始 panResponder 中有保存 InteractionManager.createInteractionHandle 返回的
handle 的信息,重新创建后保存的 handle 信息被清空,导致结束滑动时找不到 handle,所未能执行 InteractionManager.clearInteractionHandle 动作。

下面是在 PanResponder.js 中添加了打印 log 输入的结果:

debug2

解决方案:把下面 movingzooming 的定义由 useState 改为 useRef 的方式,避免重复 PanResponder.create

const [zooming, setZooming] = useState(false);
const [moving, setMoving] = useState(false);

更改后的打印日志信息:

debug3

这样就可以正常 InteractionManager.createInteractionHandle 了。

@zhiqingchen
Copy link
Member

@zhiqingchen 你好,经过这边调试,确系是下面这个函数的问题

export function usePanResponder(
dispatchEvents: DispatchEvents
): [PanResponderInstance] {
const [zooming, setZooming] = useState(false);
const [moving, setMoving] = useState(false);
const pan = useRef({
initialX: 0,
initialY: 0,
prevDistance: 0,
});
const panResponder = useMemo(
() =>
PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: ({ nativeEvent }) => {
dispatchEvents(['mousedown', 'mousemove'], nativeEvent);
},
onPanResponderMove: ({ nativeEvent }) => {
const touches = nativeEvent.touches;
const length = touches.length;
if (length === 1) {
if (!moving || zooming) {
setMoving(true);
setZooming(false);
} else {
dispatchEvents(['mousemove'], nativeEvent);
}
} else if (length === 2) {
const [
{ locationX: x0, locationY: y0 },
{ locationX: x1, locationY: y1 },
] = touches as [NativeTouchEvent, NativeTouchEvent];
const distance = calcDistance(x0, y0, x1, y1);
const { x, y } = calcCenter(x0, y0, x1, y1);
if (!zooming) {
pan.current = {
initialX: x,
initialY: y,
prevDistance: distance,
};
setZooming(true);
} else {
const { initialX, initialY, prevDistance } = pan.current;
const delta = distance - prevDistance;
pan.current.prevDistance = distance;
dispatchEvents(['mousewheel'], nativeEvent, {
zrX: initialX,
zrY: initialY,
zrDelta: delta / 120,
});
}
}
},
onPanResponderTerminationRequest: () => true,
onPanResponderRelease: ({ nativeEvent }) => {
if (!zooming) {
dispatchEvents(['mouseup', 'click'], nativeEvent);
}
setMoving(false);
setZooming(false);
},
onPanResponderTerminate: () => {},
onShouldBlockNativeResponder: () => {
return false;
},
}),
[dispatchEvents, moving, zooming, pan]
);
return [panResponder];
}

由于创建 panResponder 使用 useMemo 依赖了 moving, zooming, 从按下到滑动过程中 moving 的值发生变更,导致 PanResponder.create 重新创建,由于原始 panResponder 中有保存 InteractionManager.createInteractionHandle 返回的

handle 的信息,重新创建后保存的 handle 信息被清空,导致结束滑动时找不到 handle,所未能执行 InteractionManager.clearInteractionHandle 动作。

下面是在 PanResponder.js 中添加了打印 log 输入的结果:

debug2

解决方案:把下面 movingzooming 的定义由 useState 改为 useRef 的方式,避免重复 PanResponder.create

const [zooming, setZooming] = useState(false);
const [moving, setMoving] = useState(false);

更改后的打印日志信息:

debug3

这样就可以正常 InteractionManager.createInteractionHandle 了。

是这个原因

@ekoooo
Copy link
Author

ekoooo commented Jul 11, 2023

try @wuba/react-native-echarts@1.2.2-alpha.0 @ekoooo

经过测试没有问题了,感谢!

@ekoooo ekoooo closed this as completed Jul 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants