The objective is to create an input component where the user can click a button, which if held, would fire an action multiple times (e.g. think here of a + button, which would increment continuously the parent's value). The firing stops when the button is released.
The below code is the minimum code to highlight both the needed infrastructure (parent/child) and the issue I am facing.
The issue: when pressing, the count value is incremented only once, although the incrementor is fired multiple times. The parent owns both the count value and the onChange callback (along the <TextInput> pattern)
I believe it comes from that the value in incr() is set to the value at MyInput render time; As long as the button is not released, MyInput is not re-rendered, hence the incrementor is still bound to that original value.
Parent Component:
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>
)
}
Child Component: MyInput follows <TextInput> and similar patterns where the value is owned by the parent and updated via an onChange callback
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>
)
};
I could partially solve the situation if I use a local state copy of the value (say [ localValue, setLocalValue ] = useState(value) in MyInput, and use setLocalValue(xxx => xxx+1) ... however it would only update the local value and not the parent's one.
Maybe force refreshing would be a (bad) option too?
I tried around with useCallback/... however, as it seems, I would need to release the button before the parent gets re-rendered (or something like that).
The constraints are: functional-components & hooks and the value/onChange parent owned props
Any idea how I could solve the above situation? thanks in advance for any hints