how to click a button programmatically in react native - reactjs

i have tried couple of ways to figure out how to perform click event programmatically, i found that useRef can solve the problem. but i tried it doesn't seems to work for me. below is the code,
//declaration
const reff=useReff(null);
//button design
<Button
onPress={()=>{console.log('Pressed')}}
title="Press me"
ref={reff}
/>
//Triggering the event
reff.current.onPress();
its giving me an error "onPress is undefined". i have tried with onClick(),click(),press() nothing worked for me unfortunately.
can someone help me on this please..

import React, { useRef } from 'react';
import { Button, View } from 'react-native';
const App = () => {
const btnRef = useRef();
return (
<View>
<Button
title="Ref"
onPress={() => {
console.info('Ref props', btnRef.current.props);
btnRef.current.props.onPress();
btnRef.current.props.onHandlePress();
}}
/>
<Button
title="Click Me"
ref={btnRef}
onPress={() => console.log('onPress props called')}
onHandlePress={() => console.log('Custom onHandlePress called')}
/>
</View>
);
};
export default App;
I'm using react native cli
"react": "16.13.1",
"react-native": "0.63.4"
I guess, whatever props that passed in Button component can be accessed by calling ref.current.props.anyprops, Good luck!

Try this once, you just need to set ref :
<Button id={buttonId}
onClick={e => console.log(e.target)}
ref={(ref) => { this.myButton = ref; }}
/>
and then you can add this in your function :
yourFunction = () => {
this.myButton.click()
}
Hope it helps. feel free for doubts

This works for me:
<Button
onClick={() => console.log('pressed')}
title="Press me"
/>
Just use the curly braces when you write the callback functions on multiple lines.

Related

Window is undefined in NextJS + Tauri

What I'm trying to do:
I was using the appWindow from Tauri to access the appWindow.minimize(), appWindow.toggleMaximize(), and appWindow.close() to create a custom title bar.
What the code looks like:
import { appWindow } from "#tauri-apps/api/window";
const CustomTitleBar = () => {
const hasLoaded = hasLoadedCSR(); // custom hook for checking if component has mounted using useEffect
if (typeof window === "undefined") return <></>; // 1st attempt to disable SSR for this component
if (!hasLoaded) return <></>; // 2nd attempt to disable SSR for this component
return (
<>
<div data-tauri-drag-region className="titlebar">
<button
className="titlebar-button"
id="titlebar-minimize"
onClick={() => {
console.log("Clicked");
appWindow.minimize();
}}
>
<img
src="https://api.iconify.design/mdi:window-minimize.svg"
alt="minimize"
/>
</button>
<button
className="titlebar-button"
id="titlebar-maximize"
onClick={() => appWindow.toggleMaximize()}
>
<img
src="https://api.iconify.design/mdi:window-maximize.svg"
alt="maximize"
/>
</button>
<button className="titlebar-button" id="titlebar-close">
<img
src="https://api.iconify.design/mdi:close.svg"
alt="close"
onClick={() => appWindow.close()}
/>
</button>
</div>
</>
);
};
export default CustomTitleBar;
I basically did 2 attempts to solve the problem because I definitely think this is caused by SSR as mentioned by FabianLars in a similar question.
To fix the problem, I basically created another component using the dynamic function to force CSR for CustomTitleBar.
import dynamic from "next/dynamic";
const DynamicCustomTitleBar = dynamic(() => import("./CustomTitleBar"), {
ssr: false,
});
export default DynamicCustomTitleBar;
How I got around this issue is as follows.
If and when I use any of the #tauri-apps/api exports I wrap them in a component that I then import using the dynamic import flow without ssr. This is mainly to keep my project organized because I just create sort of a wrapper for every function that I might want to use from tauri-apps/api.
So I would make the following folder structure:
const TauriApiWrapComponent = dynamic(() => import("./TauriApiWrapComponent"), {
ssr: false,
});

How to write test for a button inside a list tag? Unable to get the button element inside a ui tag?

checkResult is a helper function which is imported in my component.jsx
component.jsx
return(
<ul>
{options.map((option) => {
return (
<li key={option.value}>
<button
data-testid="unlock-btn"
onClick={() => {
checkResult()
? lunch(option.value)
: showError();
}}
>
{option.label}
</button>
</li>
);
})}
</ul>;
)
my test
import * as helper from "../helpers/checkResult";
it("fires action when lunch is clicked", async () => {
const spy = jest.spyOn(helper, 'checkResult');
let component;
await act(async()=>{
component = <component /> ;
})
await expect(screen.queryByTestId("unlock-btn"));
fireEvent.click(screen.queryByTestId("unlock-btn"));
expect(spy).toHaveBeenCalled();
});
this is the error i'm getting
Unable to fire a "click" event - please provide a DOM element.
i have also provided my getComponent Method above
You're not providing options to the component so it has nothing to render. You're also using a map to render a list of items all of which have the same id. You should do something like
map((option, index) => {
return (
<li key={option.value}>
<button
data-testid=`unlock-btn-${index}`
This way you can target each individual option by ID in your test.
Edit: Your fireEvent is not defined in your example either.
The right way would be using the aria-label and attributes to be able to select those buttons without the need of data-testid.
<button
onClick={() => { checkResult() ? lunch(option.value): showError();}}
name={option.label} // assuming the labels are unique
>
{option.label}
</button>
then:
import React from 'react';
import { render, screen, fireEvent } from '#testing-library/react';
it('Should do some test', ()=>{
render(<MyComponent/>)
const button = screen.getByRole('button', {name: "some-label"})
fireEvent.click(button)
expect(....).toBe(...)
}

React Native TextInput onFocus does not allow onPress of other child components

Nested TextInput component does not allow other components' onPress function to be called. Only when the TextInput is not focused, the onPress works fine.
React Native Version : 0.66.3
Here is my code
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClearSearchText}>
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})
Attached are the images of
Expected.
VS
The issue
When the cross icon is pressed on focused state, the textInput is unfocused rather What I am trying to achieve is that the search text gets cleared & the input remains focused.
Note: The onPress works perfectly fine when input is not focused
Any help is appreciated. Thanks!
PS: Tried TouchableOpacity & also I have tried wrapping the components inside a ScrollView to use keyboardShouldPersistTaps='handled' as mentioned in one of the SO answer but to no success.
Found a workaround to this is to wrap the whole component into a ScrollView and adding the prop keyboardShouldPersistTaps='handled'
Previously I was making the View inside the Component as ScrollView and adding keyboardShouldPersistTaps='handled' which did not work
export const Component = (props): JSX.Element {
...
return (
<ScrollView contentContainerStyle={styles.searchBoxContainer}
keyboardShouldPersistTaps='handled'>
...
</ScrollView>
)
})
The key was to wrap the entire component inside the ScrollView,
Here's what worked:
<ScrollView keyboardShouldPersistTaps='handled'>
<Component {...props}/>
</ScrollView>
Guess this was a silly mistake, but worth pointing out!
You're setting the focus of the entire component based on whether the TextInput has focus. Since the clear button is outside the text input, pressing it causes the component to lose focus.
One solution is to store the TextInput instance in a ref. When the clear button is pressed, you can refocus the text input. I've copied your component below and added some new lines, which are marked in comments.
export const Component = (props): JSX.Element {
const { searchText, onChangeSearchText, onClearSearchText } = props
const textInputRef = useRef(null); // new
const [searchViewFocused, setSearchViewFocused] = useState<boolean>(false)
const searchIcon = searchViewFocused ? "searchActive" : "searchInactive"
const cancelIcon = searchViewFocused ? "cancelActive" : "cancelInactive"
const onClear = () => {
onClearSearchText();
textInputRef.current?.focus();
} // new
return (
<View style={styles.searchBoxContainer}>
<View style={[styles.searchBox, searchViewFocused && styles.searchBarActive]}>
<Icon styles={styles.searchIcon} icon={searchIcon} />
<TextInput
ref={textInputRef} // new
style={styles.searchInput}
onChangeText={onChangeSearchText}
value={searchText}
onFocus={() => setSearchViewFocused(true)}
onBlur={() => setSearchViewFocused(false)}
autoCompleteType={"off"}
numberOfLines={1}
/>
{searchText !== "" && (
<Pressable style={styles.clearIcon} onPress={onClear}> // calls new function
<Icon icon={cancelIcon} />
</Pressable>
)}
</View>
</View>
)
})

ToggleButtons in react bootstrap won't reset

Hello I am trying to make a component out of react bootstrap toggle group buttons using formik, the component works and pass the data correctly, but it just won't reset the buttons to "no button pressed at all" when I reset the form (the last pressed button is still checked,although the value of the togglebuttons being null(as intended)).
import React, { useState, useEffect } from "react";
import "./style.scss";
import { ToggleButtonGroup, ToggleButton } from "react-bootstrap";
const ToggleButtonsGrp = (props) => {
const [toggleValue, setToggleValue] = useState(props.state);
useEffect(() => {
props.formik.setFieldValue(props.formName, JSON.parse(toggleValue));
props.formik.setFieldTouched(props.formName);
}, [toggleValue]);
return (
<ToggleButtonGroup
id="toggleButtonGroup"
name="toggleButtonGroup"
className="toggleButtonGroup"
onChange={(value) => setToggleValue(value)}
>
{props.toggleButtons.map((opt) => (
<ToggleButton
key={opt.name}
id={`toggle-${opt.name}`}
type="radio"
name={props.formName}
className={`toggleBtn ${
opt.value === toggleValue ? "toggleBtn-Off" : "toggleBtn-active"
}`}
checked={opt.value === toggleValue}
value={opt.value}
>
{opt.name}
</ToggleButton>
))}
</ToggleButtonGroup>
);
};
tried to add a useEffect like this:
const [update,SetUpdate] = useState(0);
useEffect(() => {
if(!props.state) //when it resets or first time
setUpdate(update+1) //this would force a rerender
}, [props.state]);
It didn't solve the problem so I removed this.
The external form looks like this:
<Formik
initialValues={someFieldName:null}
onSubmit={(values, { resetForm }) => {
...//some logic
resetForm()
...
}}
>
{(formik)=>(
...
<ToggleButtonsGrp
toggleButtons={SomeButtons}
formName="someFieldName"
state={formik.values.someFieldName}
formik={formik}
/>
..
)}
</Formik>
while SomeButtons built like:
[{
name:"someName",
value:"someValue"
},
{
name:"someName2",
value:"someValue2"
}]
In the end I don't need to reset the form, leaving it if someone wants a challenge.
Sadly can't close the question myself.

How catch event onScoll or onScollEnd etc of HScrollview

I am using
import {CollapsibleHeaderTabView} from "react-native-tab-view-collapsible-header";
import {HScrollView} from "react-native-head-tab-view";
I push a FlastList into HScrollView
<HScrollView
index={0}
style={{backgroundColor: "#FFFFFF"}}
// onScroll={(event)=>console.log("hitme2")}
// onScroll={()=>console.log("hitme")}
>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={item => item.id}
onEndReached={onEndReached}
/>
</>
Event onEndReached of flashlist will not working instead of that scroll off Hscrollview working.
I want catch event of Hscollview to make custom same onEndReached.
Please help me!
React: "16.13.1"
React Native: "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz"
React-Native-Gesture-Handler: "^1.10.3"
You're in luck because I recently had a similar problem and found a solution :)
// imports
import React, {useCallback} from "react";
import {runOnJS, useAnimatedReaction, useSharedValue} from "react-native-reanimated";
import {CollapsibleHeaderTabView} from "react-native-tab-view-collapsible-header";
// component logic
const doSomething = useCallback((...args) => {/* do something */}, []);
const [scrollTrans, setScrollTrans] = useState(useSharedValue(0));
useAnimatedReaction(() => scrollTrans.value, (scrollTransValue) => {
console.log('scroll value', scrollTransValue);
/* do something here */
runOnJS(doSomething)(/* put args here */);
}, [doSomething, scrollTrans]);
const makeScrollTrans = useCallback((scrollTrans) => {
setScrollTrans(scrollTrans);
}, []);
// CollapsibleHeaderTabView
<CollapsibleHeaderTabView {...args} makeScrollTrans={makeScrollTrans}/>
You need to call some functions inside runOnJS function because react-native-reanimated since v2 running on a different thread
Also, instead of HScrollView you can use HFlatList like this:
<HFlatList
index={0}
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
onEndReached={onEndReached}
{/* rest FlatList props */}
/>

Resources