目标是创建一个输入组件,用户可以在其中单击一个按钮,如果按住该按钮,将多次触发操作(例如,在这里考虑一个+
按钮,它将连续递增父级的值(。松开按钮时,触发停止。
下面的代码是突出显示所需的基础结构(父/子(和我面临的问题的最低代码。
问题:按下时,count
值仅递增一次,尽管增幅器被多次触发。父级同时拥有count
值和onChange
回调(沿<TextInput>
模式(
我相信这是因为incr()
中的value
设置为渲染时的值MyInput
;只要不释放按钮,MyInput
就不会重新渲染,因此增量器仍绑定到该原始value
。
父组件:
const Screen = ({}) => {
const [ count, setCount ] = useState(0);
const onChange = (value) => {
setCount(value);
}
return (
<View style={{ flex:1, justifyContent:'center', alignItems:'center' }}>
<Text>Parent: [{count}]</Text>
<MyInput value={count} onChange={onChange}/>
</View>
)
}
子组件:MyInput
遵循<TextInput>
和类似的模式,其中value
由父组件拥有并通过onChange
回调进行更新
export const MyInput = ({ value, onChange }) => {
const [ timer, setTimer ] = useState(null);
const incr = () => {
const nv = value + 1;
console.warn(new Date(), `nv: ${nv}`);
onChange(value + 1);
}
const onPressIn = () => {
setTimer(setInterval(incr, 10));
// or calling incr() twice here, would have the same effect
};
const onPressOut = () => {
clearInterval(timer);
};
return (
<View>
<Text>Child: {value}</Text>
<TouchableOpacity
onPressIn={onPressIn}
onPressOut={onPressOut}>
<Text>+</Text>
</TouchableOpacity>
</View>
)
};
如果我使用值的本地状态副本(例如[ localValue, setLocalValue ] = useState(value)
在MyInput
中使用setLocalValue(xxx => xxx+1)
,我可以部分解决这种情况......但是,它只会更新本地值,而不会更新父值。
也许强制刷新也是一个(糟糕的(选择?
我尝试了使用回调/...但是,看起来,我需要在重新渲染父级(或类似的东西(之前释放按钮。
约束条件是:功能组件和钩子以及父级拥有的value/onChange
道具
知道我该如何解决上述情况吗? 提前感谢您的任何提示
import {useEffect}from 'react'
export const MyInput = ({ value, onChange }) => {
const [timer, setTimer] = useState(null);
const incr = () => {
const nv = value + 1;
// console.warn(new Date(), `nv: ${nv}`);
onChange(value + 1);
}
useEffect(() => {
incr()
setTimer(setInterval(incr, 10));
}, [value])
const onPressIn = () => {
// setTimer(setInterval(incr, 10));
// or calling incr() twice here, would have the same effect
onChange(value + 1);
};
const onPressOut = () => {
clearInterval(timer);
};
return (
<View>
<Text>Child: {value}</Text>
<TouchableOpacity
onPressIn={onPressIn}
onPressOut={onPressOut}>
<Text>+</Text>
</TouchableOpacity>
</View>
)
};