React Native: onPressOut is not triggered - javascript

I have this component that is supposed to continuously increment as long as the button is being pressed and stop the event when the button is released onPressOut:
<TouchableOpacity
onPress={() => setCount((b) => b + 1)}
onLongPress={() => {
let id = setInterval(() => {
setCount((b) => b + 1);
}, 500);
intervalIdRef.current = id;
}}
onPressOut={() => {
console.log("INTERVAL ID current I: ", intervalIdRef.current);
clearInterval(intervalIdRef.current);
console.log("INTERVAL ID current II: ", intervalIdRef.current);
}}
>
<Text>plus</Text>
</TouchableOpacity>
The problem is the continuous increment works as expected onLongPress, but the event does not stop when the long press is released and the onPressOut does not even fire at all. What am I missing here?

Related

How to limit a textarea to 4 rows of bullet points?

I was wondering if it is possible to limit a user to only enter 4 lines in a text area. I have tried using maxRows, but that isn't working as I thought. Notice how I have put maxLength to 9999999, as I don't mind how much text is entered on each bullet point, I just want to limit it to a maximum of 4 new line characters/bullet points. If anyone has a solution to how I could accomplish this, that would be great.
<TextField
onKeyUp={handleInput}
inputProps={{
maxLength: 9999999
}}
sx={{ ...fieldCSS, width: '100%', marginTop: '6px' }}
multiline
rows={4}
value={details}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setDetails(event.target.value);
setCalled({ ...called, detail: true });
}}
error={!canSubmitDetails && called.detail}
helperText={detailsHelperText}
/>
See in the image below, I dont want the user being able to enter that 4th bullet point.
EDIT: after adding the suggested answer with a minor edit but event.stopPropagation(); isnt working
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
const { code, target } = event;
const { value } = target as HTMLInputElement;
if (code === 'Enter') {
if ([...value].filter((word) => word === '\n').length >= DETAIL_MAX_LINES) {
event.stopPropagation();
}
}
};
Cheers,
Has400
You can use the onKeyDown event to check if the user is pressing the enter key. If so, you can check if the number of lines is equal to 4. If so, you can prevent the default behavior of the enter key.
const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === 'Enter') {
const lines = event.currentTarget.value.split('\n');
if (lines.length === 4) {
event.preventDefault();
}
}
};
<TextField
onKeyDown={handleKeyDown}
...
/>
EDIT: It doesn’t look like the enter key method is working, so try using the onChange event to check if the number of lines is greater than 4.
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
if ([...value].filter((word) => word === '\n').length >= 4) {
event.stopPropagation();
}
};
<TextField
onChange={handleChange}
...

Make table scroll with selected table row with onKeyDown (React)

I'm having some major difficulties trying to make my table scroll when going through
the table rows with onKeyDown. What seems to be happening is that the event doesn't actually change when using the keyboard, even though selected is highlighting the selected row properly. If a table row in the middle of the table is clicked, the table will scroll only the first time the keyboard up or down is pressed. (which makes sense since its a new event).
I've tried wrapping the function handleKeyDown in useCallback in case it lost a reference between renders, but it didn't matter.
Does anyone have any suggestions on how I can get a new event on key press?
I'm leaving the main functions here to have a look at, however you can find a working
Sandbox with the problem here: https://codesandbox.io/s/basictable-demo-material-ui-forked-54frpm?file=/demo.tsx
const handleKeyDown = (event) => {
event.preventDefault();
console.log(event.target);
scrollIntoView(event.target);
if (event.key === "ArrowUp") {
if (selected === rows[0].id) return;
setSelected(getNextRow(rows, selected, "up"));
}
if (event.key === "ArrowDown") {
if (selected === rows[rows.length - 1].id) return;
setSelected(getNextRow(rows, selected, "down"));
}
};
<TableRow
key={row.id}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
selected={isRowSelected(row.id)}
onClick={() => handleSetSelected(row.id)}
onKeyDown={handleKeyDown}
tabIndex={0}
const isRowSelected = (rowId) => {
return rowId === selected;
};
const scrollIntoView = (element) => {
if (element) {
element.scrollIntoView({ behavior: "smooth", block: "center" });
}
};
const getNextRow = (rows, selected, direction) => {
const index = rows.findIndex((row) => row.id === selected);
if (direction === "up") return rows[index - 1].id;
if (direction === "down") return rows[index + 1].id;
}
The events are logged into the console. If you click the first row, press the down arrow on your keyboard 2-3 times, you can inspect the logged elements and confirm they are all from the row that was actually clicked on, and not the ones selected with the keyboard.
Thanks for your help in advance.
Stephan Bakkelund Valois
I found a working solution that doesn't depend on the keyDown event itself.
As we're already calculating the next row with the function getNextRow that returns the id of our next row, we can utilize this to make it scroll.
First, we need to add the id our table row component:
<TableRow
....
id={row.id}
>
We can send the id we got from getNextRow to the scrollIntoView function, and fetch the html element with a querySelector, and that way scroll to the correct row:
const scrollIntoView = (id: string) => {
const el = document.querySelector(`#${id}`)
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' })
}

React Native eventEmitter.removeListener deprecated error

I am getting an deprecated error when using the follow code for removing event listener. When I try to use it anyway nothing happens. Can someone help me fix the code block so that I remove the event listen correctly. I am using this in a functional component
remove
eventEmitter.removeListener("PreparePaywallFinished", onPreparePaywallFinished);
add listener
eventEmitter.addListener("PreparePaywallFinished", onPreparePaywallFinished);
Here is same of the code I am working with
// raise paywall by event listener then clear listener after paywall is raised
const onPreparePaywallFinished = result => {
if (result.success == true) {
NativeModules.NamiPaywallManagerBridge.raisePaywall();
} else {
console.log('error is ' + result.errorMessage);
}
eventEmitter.addListener('PreparePaywallFinished', onPreparePaywallFinished)
};
// create event listener on button press. also emit event
const _handlePress = () => {
console.log('pressed');
const subscription = eventEmitter.addListener('PreparePaywallFinished', onPreparePaywallFinished); //prettier-ignore
NativeModules.NamiPaywallManagerBridge.preparePaywallForDisplay(true, 2); //prettier-ignore
};
// button to be pressed
return(
<View>
<Button title="press me" onPress={() => press()} />
</View>
)
According to Native documents, this has been deprecated and not used. You can use .remove()

ReactJS - Conditional rendering not working

Basically, the code snippet below is for the Close/Edit icon: Once clicked in "Close" mode, it will change to the "Edit" icon and pass the rowId and a "1" parameter in the handleEdit function; and, once in "Edit" mode, it will pass the rowId and a "0" parameter.
The problem is that it only goes to the condition of editClose === 1, although it updates the editDeleteTag to 1; it never seems to render the stylesEditOptions icon along with it's condition.
I'm new to React, so I might be missing something here.
childComponenent.jsx
funcEdit = (editClose) => {
if (editClose === 0) {
return (<div className={styles.editOptions}>
<Input type="button" className={styles.closeIcon} onClick={() => this.props.handleEdit(rowIndex, 1)} />
</div >)
} else {
return (<div className={styles.editOptions}>
<Input type="button" className={styles.EditIcon} onClick={() => this.props.handleEdit(rowIndex, 0)} />
</div >)
}
}
render()
let locArr = [...this.state.mainArray];
For looop .... {
if (locArr[i].editOrDeleteTag === 0) {
locArr[i].editOrDelete = this.funcEdit(1);
} else {
locArr[i].editOrDelete = this.funcEdit(0);
}
}
return(
...
<BootstrapTable data={locArr}
...
)
parentComponent.jsx
handleEdit = (rowId, toggle) => {
let locArr = [...this.state.mainArray];
locArr[rowId.rowIndex].editOrDeleteTag = toggle
this.setState({ mainArray : locArr });
};
The most likely reason is the fact that you are mutating the state.
When making state changes to a nested object you need to update all parent elements.
So in your handleEdit try to use
locArr[rowId.rowIndex] = {
...locArr[rowId.rowIndex],
editOrDeleteTag: toggle
};
instead of
locArr[rowId.rowIndex].editOrDeleuteTag = toggle;

Send cursor to the end of input value in React

I am dynamically passing a value to my input field when clicking delete (in order to edit the last input entry).
I can see that in Chrome once the input value gets rendered the cursor shows up a the beginning of the word, while in Safari and Firefox goes at the end of the value but the last letter gets deleted.
How can I always see the cursor at the end without deleting the last letter(unless I hit backspace twice)?
tagEvent(e) {
const tag = this.text.value;
const tagGroup = tag.split(" ");
const tiles = this.props.tiles;
const hasTiles = Object.keys(tiles).length > 0;
if(e.keyCode === 32 || e.keyCode === 13){
e.preventDefault();
tagGroup.map(tag => this.props.addTile(tag));
this.tagForm.reset();
}
if(e.keyCode === 8 && hasTiles && tag === '' ) {
this.props.editLastTile();
this.tagForm.reset();
}
}
render() {
return (
<div className="input-wrapper">
<form ref={(input) => this.tagForm = input}>
<input ref={(input) => this.text = input}
type="text"
name="new-item"
placeholder="type and press space"
autoComplete="off"
defaultValue={this.props.value}
onKeyDown={(e) => this.tagEvent(e)} />
</form>
</div>
)
}
Here a Pen with the full code
Thanks a lot for the help!
Another simple solution:
<input ref={ref => ref && ref.focus()}
onFocus={(e)=>e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length)}
/>
ref triggers focus, and that triggers onFocus to calculate the end and set the cursor accordingly.
You can explicitly set cursor position, to do so add this to Input:
componentDidUpdate(prevProps) {
if (prevProps.value !== this.props.value) {
this.text.selectionStart = this.text.value.length;
this.text.selectionEnd = this.text.value.length;
}
}
To prevent removing last character add a e.preventDefault() after if(e.keyCode === 8 && hasTiles && tag === '' ) {
Edited Pen
For those of you coming here trying to use this with react hooks 🙌
A simple texfield component that toggles the type of the input to password/text, this is the typical case where you would like to allow users to see their password by clicking on a button to toggle the type and see the value.
function TextField() {
const [type, setType] = useState('text');
const inputRef = useRef(null);
const onToggle = useCallback(() => {
setType(current => type === 'text' ? 'password' : 'text');
// Setting focus here
inputRef.current.focus();
}, []);
useEffect(() => {
// Moving cursor to the end
inputRef.current.selectionStart = inputRef.current.value.length;
inputRef.current.selectionEnd = inputRef.current.value.length;
}, [type]);
return (
<div>
<input
ref={inputRef}
type={type}
/>
<button onClick={onToggle}>toggle type</button>
</div>
);
}

Categories