我正在使用 React Native 的 Pan Responder。当拖动其子项中的 Pan Responder 时,我需要更新父项中的某些状态。
复杂的一点是我还需要这些孩子将他们自己的元素插入可拖动区域。
对于上下文,这里是一个更简单的例子,可以正常工作 https://snack.expo.io/@jamesweblondon/drag-items
import * as React from 'react';
import { useRef, useState } from 'react';
import { Text, View, PanResponder } from 'react-native';
const items = ['1', '2', '3'];
const ITEM_HEIGHT = 100;
const Parent = () => {
const [y, setY] = useState(0);
const [index, setIndex] = useState(null);
return (
<View style={{ marginTop: 50 }}>
<Text>Index: {index}</Text>
<Text>Y: {y}</Text>
<View
style={{ height: ITEM_HEIGHT * items.length, backgroundColor: 'gold' }}>
{items.map((item, itemIndex) => {
const isBeingDragged = itemIndex === index;
const top =
isBeingDragged
? (ITEM_HEIGHT * itemIndex) + y
: (ITEM_HEIGHT * itemIndex);
return (
<View
style={{
top,
width: '100%',
position: 'absolute',
zIndex: isBeingDragged ? 1 : 0
}}
key={itemIndex}>
<Child
index={itemIndex}
setIndex={setIndex}
setY={setY}
item={item}
/>
</View>
);
})}
</View>
</View>
);
};
const Child = ({ index, setIndex, setY, item }) => {
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
setIndex(index);
},
onPanResponderMove: (evt, gestureState) => {
setY(gestureState.dy);
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
setY(0);
setIndex(null);
},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => true,
})
).current;
return (
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: 'tomato',
padding: 10,
borderBottomColor: 'black',
borderBottomWidth: 1,
height: ITEM_HEIGHT,
}}>
<View
{...panResponder.panHandlers}
style={{ background: 'grey', height: '100%', width: 40 }}
/>
<Text>Child {item}</Text>
</View>
);
};
export default Parent;
Run Code Online (Sandbox Code Playgroud)
这是我的完整示例:https : //snack.expo.io/@jamesweblondon/drag2
import * as React from 'react';
import { useRef, useState } from 'react';
import { Text, View, PanResponder } from 'react-native';
const CHILD_A = 'CHILD_A';
const CHILD_B = 'CHILD_B';
const CHILD_A_HEIGHT = 100;
const CHILD_B_HEIGHT = 200;
const items = [
{ type: CHILD_A, text: '1' },
{ type: CHILD_B, text: '2' },
{ type: CHILD_A, text: '3' },
];
const Parent = () => {
const [y, setY] = useState(0);
const [index, setIndex] = useState(null);
const heights = items.map((item) =>
item.type === CHILD_A ? CHILD_A_HEIGHT : CHILD_B_HEIGHT
);
let heightsSum = 0;
const heightsCumulative = heights.map(
(elem) => (heightsSum = heightsSum + elem)
);
return (
<View style={{ marginTop: 50 }}>
<Text>Index: {index}</Text>
<Text>Y: {y}</Text>
<View style={{ height: heightsSum, backgroundColor: 'gold' }}>
{items.map((item, itemIndex) => {
if (item.type === CHILD_A) {
return (
<ChildA
index={itemIndex}
setIndex={setIndex}
setY={setY}
text={item.text}
DragHandle={(props) => (
<DragHandle
{...props}
index={itemIndex}
setIndex={setIndex}
setY={setY}
/>
)}
/>
);
}
return (
<ChildB
index={itemIndex}
setIndex={setIndex}
setY={setY}
text={item.text}
DragHandle={(props) => (
<DragHandle
{...props}
index={itemIndex}
setIndex={setIndex}
setY={setY}
/>
)}
/>
);
})}
</View>
</View>
);
};
const DragHandle = ({ index, setIndex, setY, children }) => {
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
setIndex(index);
},
onPanResponderMove: (evt, gestureState) => {
console.log(gestureState.dy); // This works when the line below is removed :)
setY(gestureState); // This does not work :(
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => true,
})
).current;
return (
<View
{...panResponder.panHandlers}
style={{ background: 'grey', height: '100%', width: 40, padding: 10 }}>
{children}
</View>
);
};
const ChildA = ({ index, setIndex, setY, text, DragHandle }) => {
return (
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: 'gold',
padding: 10,
borderBottomColor: 'black',
borderBottomWidth: 1,
height: CHILD_A_HEIGHT,
}}>
<DragHandle>
<View
style={{ backgroundColor: 'goldenrod', width: '100%', height: '100%' }}
/>
</DragHandle>
<Text>Child A: {text}</Text>
</View>
);
};
const ChildB = ({ index, setIndex, setY, text, DragHandle }) => {
return (
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: 'green',
padding: 10,
borderBottomColor: 'black',
borderBottomWidth: 1,
height: CHILD_B_HEIGHT,
}}>
<DragHandle>
<View
style={{ backgroundColor: 'lawngreen', width: '100%', height: '100%' }}
/>
</DragHandle>
<Text>Child B: {text}</Text>
</View>
);
};
export default Parent;
Run Code Online (Sandbox Code Playgroud)
当您拖动DragHandle组件时,这两个函数最初会触发一次,但在您继续拖动时不会再次触发:
console.log(gestureState.dy); // This works when the line below is removed :)
setY(gestureState); // This does not work :(
Run Code Online (Sandbox Code Playgroud)
如果我注释掉这一行: setY(gestureState); // This does not work :(
然后它上面的console.log 就起作用了。当您拖动它时,它会继续每秒记录多次:console.log(gestureState.dy); // This works when the line below is removed :)
因此,我认为这与在父状态更改时重新创建 Pan Responder 有关,但我不确定如何修复它。我也不确定为什么更简单的例子没有这个问题。
我通过重写你的代码来修复你的例子。您需要注意不要在每次渲染时重新创建泛响应器(这发生在您调用时setY)。您可以在每次渲染时切换乞讨者。实际上,setY正在重新渲染您的父组件,这会导致子组件的渲染
演示: https: //snack.expo.io/@nomi9995/drag2fix
import * as React from 'react';
import { useRef, useState, useEffect } from 'react';
import { Text, View, PanResponder } from 'react-native';
const CHILD_A = 'CHILD_A';
const CHILD_B = 'CHILD_B';
const CHILD_A_HEIGHT = 100;
const CHILD_B_HEIGHT = 200;
const all_items = [
{ type: CHILD_A, text: 'A', height:CHILD_A_HEIGHT },
{ type: CHILD_B, text: 'B', height:CHILD_B_HEIGHT },
{ type: CHILD_A, text: 'C', height:CHILD_A_HEIGHT },
];
const Parent = () => {
const [y, setY] = useState(0);
const [index, setIndex] = useState(null);
const [items, setItems] = useState(all_items);
const setPosition=(index,y)=>{
const _items=items;
_items[index]['position']=y;
setItems(_items)
}
const heights = items.map((item) =>
item.type === CHILD_A ? CHILD_A_HEIGHT : CHILD_B_HEIGHT
);
let heightsSum = 0;
const heightsCumulative = heights.map(
(elem) => (heightsSum = heightsSum + elem)
);
return (
<View style={{ marginTop: 50 }}>
<Text>Index: {index}</Text>
<Text>Y: {y}</Text>
<View style={{ height: heightsSum, backgroundColor: 'gold' }}>
{items.map((item, itemIndex) => {
const isBeingDragged = itemIndex === index;
const top = isBeingDragged ? item.position + y : item.position;
if (item.type === CHILD_A) {
return (
<Child
top={top}
isBeingDragged={isBeingDragged}
height={item.height}
backgroundColor="gold"
childBackgroundColor="goldenrod"
position={item.position}
setPosition={setPosition}
index={itemIndex}
setIndex={setIndex}
setY={setY}
text={item.text}
/>
);
}
return (
<Child
top={top}
isBeingDragged={isBeingDragged}
height={item.height}
backgroundColor="green"
childBackgroundColor="lawngreen"
position={item.position}
setPosition={setPosition}
index={itemIndex}
setIndex={setIndex}
setY={setY}
text={item.text}
/>
);
})}
</View>
</View>
);
};
const DragHandle = ({ index, setIndex, setY, children }) => {
const panResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
setIndex(index);
},
onPanResponderMove: (evt, gestureState) => {
setY(gestureState.dy); // This does not work :(
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
setY(0);
setIndex(null);
},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => true,
})
).current;
return (
<View
{...panResponder.panHandlers}>
{children}
</View>
);
};
const Child = ({ top, isBeingDragged, position, height, backgroundColor, childBackgroundColor, setPosition, text2, index, setIndex, setY, text }) => {
return (
<View
style={{
top,
width: '100%',
position: position?'absolute':'relative',
zIndex: isBeingDragged ? 1 : 0,
}}
key={index}
onLayout={(e)=>position!==undefined?null:setPosition(index,e.nativeEvent.layout.y)}
>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: backgroundColor,
padding: 10,
borderBottomColor: 'black',
borderBottomWidth: 1,
height: height,
}}>
<DragHandle index={index} setIndex={setIndex} setY={setY}>
<View
style={{ backgroundColor: childBackgroundColor, width: 20, height: '100%' }}
/>
</DragHandle>
<Text>Child {text2}: {index+1}</Text>
</View>
</View>
);
};
export default Parent;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
259 次 |
| 最近记录: |