我需要读取useState
中onPanResponderMove
的值。在页面加载时,onPanResponderMove
正确记录了0
的初始值。
但是,在我单击TouchableOpacity
以递增foo
之后,onPanResponderMove
仍然注销了0
,而不是新值。
export default function App() {
const [foo, setFoo] = React.useState(0);
const panResponder = React.useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {},
onPanResponderMove: (evt, gestureState) => {
console.log(foo); // This is always 0
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
})
).current;
return (
<View style={{ paddingTop: 200 }}>
<TouchableOpacity onPress={() => setFoo(foo + 1)}>
<Text>Foo = {foo}</Text>
</TouchableOpacity>
<View
{...panResponder.panHandlers}
style={{ marginTop: 200, backgroundColor: "grey", padding: 100 }}
>
<Text>Text for pan responder</Text>
</View>
</View>
);
}
答案 0 :(得分:3)
pan响应器取决于值'foo'。 useRef
在这里不是一个好选择。您应该将其替换为useMemo
const panResponder = useMemo(
() => PanResponder.create({
[...]
onPanResponderMove: (evt, gestureState) => {
console.log(foo); // This is now updated
},
[...]
}),
[foo] // dependency list
);
答案 1 :(得分:2)
问题
问题是您使用当时拥有的PanResponder
创建了foo
一次。但是,每次调用setFoo
时,您都会从useState挂钩中收到一个新的foo
。 PanResponder不会知道这个新的foo。发生这种情况的原因是useRef的工作原理,因为它为您提供了一个可变对象,该对象在整个组件生命周期中均存在。 (这在react docs here中有解释)
(您可以在此sandbox中解决问题和简单的解决方案。)
解决方案
在您的情况下,最简单的解决方案是使用从useState获得的新foo
更新PanResponder函数。在您的示例中,它看起来像这样:
export default function App() {
const [foo, setFoo] = React.useState(0);
const panResponder = React.useRef(
PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {},
onPanResponderMove: (evt, gestureState) => {
console.log(foo);
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
})
).current;
// update the onPanResponderMove with the new foo
panResponder.onPanResponderMove = (evt, gestureState) => {
console.log(foo);
},
return (
<View style={{ paddingTop: 200 }}>
<TouchableOpacity onPress={() => setFoo(foo + 1)}>
<Text>Foo = {foo}</Text>
</TouchableOpacity>
<View
{...panResponder.panHandlers}
style={{ marginTop: 200, backgroundColor: "grey", padding: 100 }}
>
<Text>Text for pan responder</Text>
</View>
</View>
);
}
注意
如果某些可变性取决于您的组件状态,请务必格外小心。如果确实需要这样做,通常最好使用getter和setter编写适当的类或对象。例如这样的东西:
const createPanResponder = (foo) => {
let _foo = foo;
const setFoo = foo => _foo = foo;
const getFoo = () => _foo;
return {
getFoo,
setFoo,
onPanResponderMove: (evt, gestureState) => {
console.log(getFoo());
},
...allYourOtherFunctions
}
}
const App = () => {
const [foo, setFoo] = React.useState(0);
const panResponder = useRef(createPanResponder(foo)).current;
panResponder.setFoo(foo);
return ( ... )
}
答案 2 :(得分:0)
您似乎正在传递foo
,以尝试更新现有状态。相反,请传递先前的状态并进行相应的更新(functional update)。像这样:
<TouchableOpacity onPress={() => setFoo(f => f + 1)}>
<Text>Foo = {foo}</Text>
</TouchableOpacity>
要在foo
处理程序中提供onPanResponderMove
的当前值。创建一个ref。
根据文档:
“ ref”对象是一个通用容器,其
current
属性是可变的,可以包含任何值
因此,考虑到这一点,我们可以编写:
const [foo, setFoo] = React.useState(0);
const fooRef = React.useRef()
React.useEffect(() => {
fooRef.current = foo
},[foo])
然后在onPanResponderMove
处理程序中,您可以访问之前设置的参考的当前值:
onPanResponderMove: (evt, gestureState) => {
console.log('fooRef', fooRef.current)
alert(JSON.stringify(fooRef))
},
工作示例here
有关过时状态处理的更多信息here
答案 3 :(得分:0)
使用useEffect
,您可以在PanResponder
更改时创建一个新的foo
。但是,我不确定这的表现如何。
export default function App() {
const [foo, setFoo] = React.useState(0);
const pan = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {},
onPanResponderMove: (evt, gestureState) => {
console.log(foo); // This is always 0
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {},
onPanResponderTerminate: (evt, gestureState) => {},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
});
const [panResponder, setPanResponder] = React.useState(pan);
useEffect(() => {
setPanResponder(pan);
}, [foo]);
return (
<View style={{ paddingTop: 200 }}>
<TouchableOpacity onPress={() => setFoo(foo + 1)}>
<Text>Foo = {foo}</Text>
</TouchableOpacity>
<View
{...panResponder.panHandlers}
style={{ marginTop: 200, backgroundColor: "grey", padding: 100 }}
>
<Text>Text for pan responder</Text>
</View>
</View>
);
}