Trigger an onPress Function from anonther component - react-native

I want to trigger an onPress function from the search icon in the navbar.
This is the search component:
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={() => {}}
/>
);
}
export default SearchIcon;
The search component is being called in the specific stack, where it's needed.
<Stack.Screen
name="Home"
component={Home}
options={({navigation, route}) => ({
...,
headerRight: props => (
<View style={{flexDirection: 'row'}}>
<SearchIcon />
<CartIcon navigation={navigation} />
</View>
),
})}
/>
On the home screen, I have an isSeacrhing constant that should change value from false to true and vice versa.
const [data, setData] = React.useState({
isSearching: false,
search: '',
...
});
// TRIGGERED BY SEARCH ICON IN NAV BAR
const toggleSearch = () => setData({...data, isSearching: !isSearching});
{data.isSearching == false ? (
<ScrollView
...
</ScrollView>
) : (
<View>
<TextInput
style={[styles.textInput, [{color: theme.text, ...FONTS.body4}]]}
value={data.search}
placeholder="Search..."
placeholderTextColor={theme.text}
onChangeText={()=>{}}
/>
</View>
)}
Is it possible to trigger the onPress function or is there another way I can make it work? The search icon is on two screen, does calling the same function make the TextInput appear on both?

Wherever you use <SearchIcon /> just add a prop in that like this
<SearchIcon onPress={() => { // Do Something }} />
Then in your SearchIcon
function SearchIcon(props) {
const theme = useSelector(state => state.themer.theme);
return (
<Icon.Button
name="search"
size={22}
color={theme.icons}
backgroundColor={theme.top_tab}
onPress={props.onPress} // Access it here like this
/>
);
}
export default SearchIcon;

Related

React Native Navigation - problem with specific screen navigation

I'm new to React Native and I'm having some difficulties with react navigation.
I'm building a weather app that fetches data from an API. There are 6 cities that I need to render to the main screen and when you press on a city it should take you to a different screen with the weather for the day for that particular city.
I can't seem to wrap my head around the last part - how to navigate to a different screen with the data when pressing on that particular city
I will appreciate any help. Thanks!
App:
const App = () => {
const [weatherData, setWeatherData] = useState([]);
const netInfo = useNetInfo();
const dataFetchAndProcess = async url => {
let data = await fetchDataFunc(url);
storeASData(data);
let takeData = await getASData();
let processData = await processDataFunc(takeData);
return setWeatherData(processData);
};
useEffect(() => {
dataFetchAndProcess(fetchUrl);
}, []);
return (
<View style={styles.container}>
<Header />
{weatherData && (
<CitiesContainer weatherData={weatherData}></CitiesContainer>
)}
</View>
);
};
CitiesContainer - generates cities from flatlist:
const CitiesContainer = ({weatherData}) => {
return (
<FlatList
style={{flex: 1, width: '100%'}}
data={weatherData}
renderItem={({item}) => (
<TouchableOpacity>
<CityContainer
date={item.date}
forecast={item.forecast}
cityName={item.cityName}
cityImg={item.cityImg}
/>
</TouchableOpacity>
)}
keyExtractor={(item, index) => index.toString()}
/>
);
};
CityContainer:
const CityContainer = ({cityName, cityImg, date, forecast}) => {
return (
<NavigationContainer>
<CityContext.Provider value={cityName}>
<Stack.Navigator initialRouteName="City">
<Stack.Screen name={cityName}>
{props => <City {...props} cityImg={cityImg} cityName={cityName} />}
</Stack.Screen>
<Stack.Screen name={`${cityName} forecast`}>
{props => (
<ForecastContainer
{...props}
date={date}
cityName={cityName}
forecast={forecast}
/>
)}
</Stack.Screen>
</Stack.Navigator>
</CityContext.Provider>
</NavigationContainer>
);
};
City:
const City = ({cityName, cityImg, date, forecast}) => {
// const cityName = useContext(CityContext);
return (
<TouchableOpacity>
<Text>{cityName}</Text>
<CityImage cityImg={cityImg} />
</TouchableOpacity>
);
};
ForecastContainer:
const ForecastContainer = ({cityName, date, forecast}) => {
return (
<View>
<Text>{cityName}</Text>
<Text>{date}</Text>
<FlatList
style={{flex: 1, width: '100%'}}
data={forecast}
renderItem={({item}) => (
<Forecast temp={item.temp} time={item.time}></Forecast>
)}
keyExtractor={(item, index) => index.toString()}
/>
{/* <Forecast forecast={forecast}></Forecast> */}
</View>
);
};
Forecast:
const Forecast = ({temp, time}) => {
return (
<View>
<Text>{temp}</Text>
<Text>{time}</Text>
</View>
);
};
After hours of research and head-banging I figured it out. I needed to pass params to the route to make it show data specific to each city:
App
<NavigationContainer>
<View style={styles.container}>
<Header />
<Stack.Navigator>
<Stack.Screen name="Home">
{props => <CitiesContainer {...props} weatherData={weatherData} />}
</Stack.Screen>
<Stack.Screen name="Forecast">
{props => (
<ForecastContainer {...props} weatherData={weatherData} />
)}
</Stack.Screen>
</Stack.Navigator>
</View>
</NavigationContainer>
CityContainer - onPress calls the Forecast page with an optional second param. The second param is crucial in this case as it's used to determine what data to render on the Forecast page
const CityContainer = ({cityName, cityImg, navigation}) => {
return (
<TouchableOpacity
onPress={() => navigation.navigate('Forecast', {cityName})}>
<City
navigation={navigation}
cityName={cityName}
cityImg={cityImg}></City>
</TouchableOpacity>
);
};
ForecastContainer - takes in the weatherData array, as well as the route arguments. The route argument is necessary to obtain the cityName param
const ForecastContainer = ({weatherData, route}) => {
const {cityName} = route.params;
const cityFinder = data => {
return data.filter(obj => obj.cityName === cityName);
};
return (
<FlatList
ListHeaderComponent={
<>
<Text>{cityName}</Text>
<Text>{cityFinder(weatherData)[0].date}</Text>
</>
}
data={cityFinder(weatherData)[0].forecast}
renderItem={({item}) => <Forecast temp={item.temp} time={item.time} />}
keyExtractor={(item, index) => index.toString()}
/>
);
};
First thing that you have to pay some attention to, is that you are creating multiple navigation containers. So, you'll not be able to navigate between screens like that. Try to move your navigation container for a high-level component, some component that wraps all your screens.
Besides, you'll need to specify the action of moving between the screens to your component on the press event.
Some documentation that can help you with more details:
Setup React Navigation: https://reactnavigation.org/docs/hello-react-navigation
Moving between screens: https://reactnavigation.org/docs/navigating/

react-navigation [v6] drawer and stack nesting issues

Like the most common use case, I want to have a drawer in my react-native application where, from all screens header I can open/close the drawer, navigate to them. Also, each screen has an option (button, card,...) that on press should navigate to any other screen.
So I have defined the following structure. But there are several problems;
Drawer cannot recognize which screen in on focus now.
Drawer type check does not give auto-suggestions on navigation prop (e.g. props.navigation.navigate(" /* no suggestion on the present screens in the stack*/"))
On Android I feel that the overall performance drops significantly
So is this a good structure? From the official documentation, I could not find any hint on how to implement it? stack nested in drawer or vise-versa?
export type MainStackParamList = {
HomeScreen: undefined;
OverViewScreen: undefined;
WorkOrdersScreen: {id?: number; description?: string; tabIndex?: number};
PropertiesScreen: undefined;
PropertyDetailScreen: {propertyUnit: PropertyUnit};
};
export type MainDrawerParamList = {MainStack: NavigatorScreenParams<MainStackParamList>};
export type AppNavigationCompositeProps = CompositeScreenProps<
DrawerScreenProps<MainDrawerParamList, 'MainStack'>,
StackScreenProps<MainStackParamList>
>;
//____The navigation part______
const MainStack = createStackNavigator<MainStackParamList>();
const Drawer = createDrawerNavigator<MainDrawerParamList>();
/* the composite type is the only way I found to have access to
drawer fucntions such as toggleDrawer in the stack screens*/
const MainStackScreens = (navigation: AppNavigationCompositeProps) => (
<MainStack.Navigator initialRouteName={'HomeScreen'} screenOptions={MainStackScreenOptions(navigation)}>
<MainStack.Screen name="HomeScreen" component={HomeScreen} />
<MainStack.Screen name="OverViewScreen" component={OverViewScreen} />
<MainStack.Screen name="WorkOrdersScreen" component={WorkOrdersScreen} />
<MainStack.Screen name="PropertiesScreen" component={PropertiesScreen} />
<MainStack.Screen name="PropertyDetailScreen" component={PropertyDetailScreen} />
</MainStack.Navigator>
);
const Navigation: React.FC<{}> = () => {
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="MainStack"
screenOptions={{headerShown: false}}
drawerContent={props => <CustomDrawerContent {...props} />}>
<Drawer.Screen name="MainStack" component={MainStackScreens} />
</Drawer.Navigator>
</NavigationContainer>
);
};
export default Navigation;
const MainStackScreenOptions = (navigation: AppNavigationCompositeProps): StackNavigationOptions => {
return {
headerStyle: {backgroundColor: '#00aade'},
headerTintColor: '#fca903',
headerTitleAlign: 'center',
headerTitleStyle: {fontWeight: 'bold', fontStyle: 'italic'},
headerBackTitle: 'GoBack',
headerLeft: () => <IconButton icon="menu" color="white" onPress={() => navigation.navigation.openDrawer()} />
};
};
//___the drawer content is like
const CustomDrawerContent: React.FC<DrawerContentComponentProps> = props => {
return (
<DrawerContentScrollView>
<Drawer.Item label="Home" onPress={() => props.navigation.navigate('HomeScreen')} icon="star" />
<Drawer.Item label="OverView" onPress={() => props.navigation.navigate('OverViewScreen')} icon="star" />
<Drawer.Item label="WorkOrders" onPress={() => props.navigation.navigate('WorkOrdersScreen')} icon="star" />
<Drawer.Item label="Properties" onPress={() => props.navigation.navigate('PropertiesScreen')} icon="star" />
</DrawerContentScrollView>
);
};

TypeError: navigation.getParam is not a function. (In 'navigation.getParam('name')', 'navigation.getParam' is undefined)

I'm nesting screens and passing props to the screens such as header title, and a render of json.
Earlier today everything was working but now it gives me the error of getParams.
HomeStack.js, here in the title I get the title by the FlatList render in the screen which navigates to this one.
<Screen
name='errorHP'
component={errorHP}
options={{
headerTitle: () => <Header navigation={navigation} title={navigation.getParam('name')} />,
headerTitleAlign: 'center',}}
/>
HP.js, here the flatlist renders and will export the render to the page errorHP
<FlatList data={filteredSearch} keyExtractor={(item) => item.key} renderItem={({item}) => (
<TouchableOpacity onPress={() => navigation.navigate('errorHP', item)}>
<Card>
<Text style={globalStyles.titleText}> {item.name} </Text>
</Card>
</TouchableOpacity>
)} />
errorHP.js, here are listed the errors and after click, will pass again params to a new page which gets the error details.
export default function errorHP ({navigation}) {
const data = navigation.getParam('errors');
const errors = Object.keys(data);
return (
<View style={globalStyles.container}>
<FlatList data={errors} renderItem={({item}) => (
<TouchableOpacity>
<Card>
<Text style={globalStyles.titleText}> {item} </Text>
</Card>
</TouchableOpacity>
)} />
I've been messing around and still cant solve this problem.
Thanks for your attention!
You need to get params from the route prop:
export default function errorHP ({navigation, route}) {
const data = route.params.errors;
// whatever
}
And
<Screen
name="errorHP"
component={errorHP}
options={({ route, navigation }) => ({
headerTitle: () => (
<Header navigation={navigation} title={route.params.name} />
),
headerTitleAlign: 'center',
})}
/>

react native - What should I do each time <textinput> is input, so <Google> in </NavigationContainer> cannot be re-rendered?

App.tsx
const Tab = createMaterialTopTabNavigator();
export default function App() {
const [text, setText] = useState('');
const [search, setSearch] = useState('');
const searchHandler = () => {
setSearch(text)
}
return (
<NavigationContainer>
<View style={styles.case1}></View>
<View style={styles.case1}>
<TextInput
style={styles.input}
placeholder='search'
onChangeText={(val) => setText(val)}
/>
<Button
onPress={searchHandler}
title="search button"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
<Tab.Navigator>
<Tab.Screen name="google1" component={() => <Google item={search} />} />
<Tab.Screen name="google2" component={() => <Google2 item={search} />} />
</Tab.Navigator>
</NavigationContainer>
);
}
Google.tsx
export default function google(props) {
return (
<View style={styles.webContainer}>
<WebView
source={{
uri:
'https://www.google.com/search?hl=en&sxsrf=ALeKk03BNJhsVEURZyXdlkOpk1l1qb2Nug%3A1595674787434&source=hp&ei=oxAcX4qzGLLxhwOhw5jwBA&q=' + props.item + '&oq=' +** props.item ** +'&gs_lcp=CgZwc3ktYWIQAzIFCC4QkwIyAggAMgIIADICCC4yAggAMgIIADICCAAyAggAMgIIADICCAA6BAgjECc6BQguEJECOgUIABCRAjoICC4QxwEQowI6BwguEEMQkwI6CAguEMcBEK8BUPEDWOkHYKwIaABwAHgAgAGMAYgBigOSAQMwLjOYAQCgAQGqAQdnd3Mtd2l6&sclient=psy-ab&ved=0ahUKEwjKkIXnn-jqAhWy-GEKHaEhBk4Q4dUDCAc&uact=5'
}}
/>
</View>
);
};
I'm creating an app that searches across different sites by typing in the search bar.
Whenever I type in the web-view keeps re-rendering. I only want to be re-rendered(Google web-view) when I click the search button.
I've heard that should use use-memo, Callback, as far as I know.
Thanks
const searchHandler = () => {
setSearch(text) <- "text" is a reference
}
try
const searchHandler = () => {
setSearch(`${text}`)
}
Prevent rerender WebView from props through useMemo. (memoization)
export default function google(props) {
const Web = React.useMemo(() => {
return <WebView source={{uri: 'abc'}} />
}, [])
return (
<View style={styles.webContainer}>
{Web}
</View>
);
};

React Native functional component navigationOptions headerRight not being set

Previously I have set the headerRight option in the root component that renders child component with Screen options like this
export default function App() {
return (
<RecipeProvider>
<NavigationContainer>
<Stack.Navigator initialRouteName="Recipes">
<Stack.Screen
name="Recipes"
component={RecipeList}
options={({ navigation }) => ({
headerRight: () => (
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("New Recipe")}
>
<FontAwesomeIcon icon={faPlus} size={20} />
</TouchableOpacity>
),
})}
/>
and now I would like to move the headerRight inside the component definition (so I don't end up with a huge App file with details that are only relevant to the screen component themselves)
I have read other solutions and tried the following
export default function RecipeList({ navigation }) {
const { recipes } = useContext(RecipeContext);
return (
<View style={styles.container}>
<FlatList
numColumns={2}
data={recipes}
keyExtractor={(recipe: Recipe) => recipe.id}
renderItem={({ item }) => {
return (
<RecipeItem
name={item.name}
minutes={item.minutes}
image={item.image}
title="Go to Detail Screen"
onPress={() => {
navigation.navigate("Recipe Details", { item });
}}
/>
);
}}
/>
</View>
);
}
RecipeList.navigationOptions = ({ navigation }) => ({
headerRight: () => (
<TouchableOpacity
style={styles.button}
onPress={() => navigation.navigate("New Recipe")}
>
<FontAwesomeIcon icon={faPlus} size={20} />
</TouchableOpacity>
),
});
But the headerRight button doesn't show no more ... any ideas ?
Thank you all <3
My headerRight is below and it works for me
EditScreen.navigationOptions = navData => {
const submitFn = navData.navigation.getParam('submit');
return {
headerTitle: 'Edit Data'
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Save"
iconName={
Platform.OS === 'android' ? 'md-checkmark' : 'ios-checkmark'
}
onPress={submitFn}
/>
</HeaderButtons>
)
};
};