Render a component onPress(TouchableOpacity/Button) in React Native - react-native

I have a functional that has a fetch call to Wordnik Api and it returns one random word. It works all fine, but am trying to set up a TouchbaleOpacity/Button that will render that component onPress so I can get a random word every time the user clicks on the button. I have the other component in a separate file, how do call it on button Press?
`export default function HomeScreen({ navigation }) {
const onPress = () => {
//return component
};
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<TouchableOpacity onPress={onPress}>
<Button title="Next word" />
</TouchableOpacity>
<Wordnik />
</View>
);
}`

Add a props key on your component and change that key every time on your button's onPress so component will render every time when key change as following :
export default function HomeScreen({ navigation }) {
const [tempKey, setTempKey] = useState(0);
const onPress = () => {
//return component
setTempKey(tempKey+1);
};
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<TouchableOpacity onPress={onPress}>
<Button title="Next word" />
</TouchableOpacity>
<Wordnik key={tempKey.toString()} />
</View>
);
}

Related

Change variable values across functions like a global variable in React Native?

I am new to React Native and I am trying to change a variable set in one function from another function. This should be similar to changing a 'global' variable to a new value. Here is my code:
import React, { useState } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
function HomeScreen({ navigation }) {
const [isLogged, setLog] = useState(0);
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
{isLogged === 0 ? (
<Button
title="Go to Login"
onPress={() => navigation.navigate('Login')}
/>
) : (
<Button title="Do Stuff" onPress={() => navigation.navigate('Stuff')} />
)}
</>
);
}
function LoginScreen({ navigation }) {
//do login stuff here...
{isLogged => setLog(1)} //unable to change 'isLogged' to a value of '1'...?
}
function StuffScreen({ navigation }) {
//do some stuff here...
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Stuff" component={StuffScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
The error I get makes it obvious the 'isLogged' variable is not recognized in the function 'LoginScreen'. What is the proper way to use and manipulate 'global' variables that can be changed across various functions in React Native? I thank you in advance for any suggestions.
Looking into a suggestion`from another user it seems the 'global' is what I need, however I am still having difficulties. Here is some adjusted code:
var func = require('./global');
global.config = func(0);
function HomeScreen({ navigation }) {
var _status = global.config;
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Passed config: {JSON.stringify(_status)}</Text>
<Text>Home Screen</Text>
</View>
{_status === 0 ? (
<Button
title="Go to Login"
onPress={() => navigation.navigate('Login')}
/>
) : (
<Button title="BroadCast" onPress={() => navigation.navigate('BroadCast')} />
)}
</>
);
}
function LoginScreen({ navigation }) {
//do login stuff here...
global.config = func(1); //new value of "1" does not pass to 'HomeScreen'
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Login Screen</Text>
<Text>Set Global: {JSON.stringify(global.config)}</Text> //indicates '0'...WHY?????
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
);
}
Here is the 'global' file where I attempt to load from external .js file:
module.exports = function(variable) {
console.log(variable);
return variable;
}
My problem here is that 'global.config' that I attempt to change in the 'LoginScreen' function does not display the updated value when returning to 'Home' screen. WHY...???? I cannot believe such a simple concept in any other language I know is nearly impossible in React Native...this is far more complicated than it should be...any advice on what I am not understanding is GREATLY appreciated...this is killing me.

Build a custom seconds and minutes input in React Native?

Im making an exercise timer and I need an input for seconds and minutes.
The ideal UX would 2 select lists side by side. Tapping on the input would open both lists, so different from the web where you can only focus on one at a time.
Ive made a demo here, although it would need to be in a modal or similar:
https://snack.expo.io/#jamesweblondon/intelligent-apple
import * as React from 'react';
import { Text, View, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
const minutes = new Array(10).fill('').map((item, index)=>{
return index;
})
const seconds = new Array(60).fill('').map((item, index)=>{
return index;
})
const Item = ({text, isSelected, onPress}) => {
return(
<TouchableOpacity
onPress={onPress}
style={[styles.item, isSelected && styles.itemIsSelected]}>
{text}
</TouchableOpacity>
)
}
export default function App() {
const [selectedMinute, setSelectedMinute] = React.useState(1);
const [selectedSeconds, setSelectedSeconds] = React.useState(10);
return (
<View style={styles.container}>
<View style={styles.row}>
<Text style={styles.heading}>Minutes</Text>
<ScrollView style={styles.scroll}>
{
minutes.map((item, index)=>{
return <Item
onPress={()=>setSelectedMinute(item)}
text={item}
key={item}
isSelected={selectedMinute === index}
/>
})
}
</ScrollView>
</View>
<View style={styles.row}>
<Text style={styles.heading}>Seconds</Text>
<ScrollView style={styles.scroll}>
{
seconds.map((item, index)=>{
return <Item
onPress={()=>setSelectedSeconds(item)}
text={item}
key={item}
isSelected={selectedSeconds === index}
/>
})
}
</ScrollView>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
height: 300
},
row: {
flex: 1,
},
scroll: {
height: 300
},
heading: {
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center'
},
item: {
padding: 30,
backgroundColor: 'grey',
borderColor: 'white',
borderWidth: 1,
justifyContent: 'center',
alignItems: 'center'
},
itemIsSelected: {
backgroundColor: 'gold'
}
});
Is it OK to build your own inputs in React Native? This would be a a bad idea on the web as it would probably result in worse UX (especially for keyboard navigation) and a component that screen readers wouldn't know how to use.
I couldn't find a library that does quite what I need. This is close but you can only have one list open at a time. It also had a major bug with Redux making it unusable for me:
https://github.com/lawnstarter/react-native-picker-select#readme
Ive been trying to use the Picker component as I imagine it's more semantic? It works how I need on iOS but not Android (see screenshots):

React Native multiple use of component defined in a parent class

Below is the code for a Header which I've defined in a common.js file:
class HeaderStyle extends Component {
render() {
return (
<Header style={{ elevation: 5 }}>
<Left style={{ flex: 1 }}>
<Icon name="md-menu" size={30} onPress={() => this.props.navigation.openDrawer()} />
</Left>
<Body style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Image style={{ width: 80, height: 45, }} source={require('./resources/hr-pro-logo.png')} />
</Body>
<Right style={{ flex: 1 }}>
<Icon name="md-search" size={30} onPress={() => alert('hi there')} />
</Right>
</Header>
);
}
}
And here is the code for my DashboardScreen.js:
export default class DashboardScreen extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<HeaderStyle /> // This is where I'm using the Header imported from common.js
<View>
<FlatList
// Code to populate my list
/>
</View>
</View>
);
}
I've been able to import the Header in my Dashboard, but when I click on the menu icon onPress={() => this.props.navigation.openDrawer()} it throws the error undefined is not an object (evaluating '_this.props.navigation.openDrawer')
Appreciate it if anyone can assist me with this issue, I'd like to be able to open the drawer on clicking the menu icon.
FYI - When I used the Header code directly in my Dashboard, the app ran smoothly with no errors. But since there are multiple screens where I want to use the Header, I need to keep it in a common place to avoid repetition of coding.
You must pass the navigation prop to your component:
<HeaderStyle navgation={this.props.navigation} />
Or, you could use the withNavigation function from react-navigation:
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return (
//you Header render
);
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
The documentation is here

React Native + Redux - onPress dispatch an action and another function

I have a list of team members that onPress will navigate to the next screen using react-navigation/StackNavigator. Depending on which team member is clicked, the next screen will have dynamically loaded the relevant team member information.
I have been able to achieve this by using Redux to change the state of who has been selected. However at the moment I am unable to figure how I can dispatch the action and trigger the function to navigate to the next screen.
Below is my component that holds the events in which I want to dispatch the action and navigation function. You can see the first team member 'Tim' has the navigation function. All other team members will dispatch their individual action types.
I'm fairly new to Redux I would appreciate if anyone could point me in the right direction of how to add these together.
The Repository can be found here - Github
import React, {Component} from 'react';
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
const styles = StyleSheet.create({
pane: {
width: 300,
height: 50,
padding: 10,
backgroundColor: 'lightgray',
alignItems: 'center',
justifyContent: 'center',
margin: 3
}
});
function navTO(passBack, location) {
const { navigate } = passBack.props.navigation;
navigate(location)
}
export default class TeamList extends Component {
constructor(props) {
super(props);
}
render() {
const { passBack, location, whoSelected, Tim, Kate, Ian, Smita, Lee, Ben } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{whoSelected}</Text>
<TouchableOpacity onPress={() => navTO(passBack, location)} style={styles.pane}>
<Text>Tim</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Kate} style={styles.pane}>
<Text>Kate</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Ian} style={styles.pane}>
<Text>Ian</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Smita} style={styles.pane}>
<Text>Smita</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Lee} style={styles.pane}>
<Text>Lee</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Ben} style={styles.pane}>
<Text>Ben</Text>
</TouchableOpacity>
</View>
);
}
}
How about something like:
export default class TeamList extends Component {
constructor(props) {
super(props);
this.showTeamMember = this.showTeamMember.bind(this)
}
showTeamMember(teamMemberCallback) {
const { passBack, location } = this.props;
return () => {
teamMemberCallback();
navTO(passBack, location);
}
}
render() {
const { Tim, Kate, Ian, Smita, Lee, Ben } = this.props;
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>{whoSelected}</Text>
<TouchableOpacity onPress={this.showTeamMember(Tim)} style={styles.pane}>
<Text>Tim</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Kate)} style={styles.pane}>
<Text>Kate</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Ian)} style={styles.pane}>
<Text>Ian</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Smita)} style={styles.pane}>
<Text>Smita</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Lee)} style={styles.pane}>
<Text>Lee</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.showTeamMember(Ben)} style={styles.pane}>
<Text>Ben</Text>
</TouchableOpacity>
</View>
);
}
}
showTeamMember creates the handler function for onPress that call both the teamMemberCallback and the navTo function.

Missing styles for item in React Native Side Menu (should indicate active route in React Native Navigator)

I'm working on an app with a Side Menu and a Navigator. In the side menu there are menu-items which let's you navigate (using Navigator), and the menu-items also get styled to indicate which one is active.
When first going to a new route with navigator.push() and then going back with navigator.pop(), the menu-item corresponding to the previously active route (which is now inactive) does not get either of the styles to show that it is either inactive or active. The style of the menu item (RouteMenuItem in the full example below) is as follows
style={[
{ padding: 15, borderColor: 'firebrick', borderWidth: 1 },
(isActive
? {backgroundColor: 'green'}
: {opacity: 0.5}
)
]}
How is it possible that neither {backgroundColor: 'green'} nor {opacity: 0.5} gets applied?
The image below shows how the bug looks on Android: "Route ONE" is selected, however, the menu item for "Route TWO" does not have opacity: 0.5 as it should (it should be half transparent like the 3rd menu item).
Below is the full code for a minimal example to reproduce the bug. The component hierarchy is like this:
<Navigator renderScene={() => <SideMenu><View /></SideMenu>} />
PS: We use StyleSheet.create() in our code, but in the example below I've just defined the styles inline. It does not seem to make any difference.
import React from 'react';
import {View, Text, TouchableHighlight, Navigator, Dimensions} from 'react-native';
import SideMenu from 'react-native-side-menu';
/***************
/** Routes **/
/****************/
const ROUTES = {
ONE: () => ({ title: 'Route ONE' }),
TWO: () => ({ title: 'Route TWO' }),
THREE: () => ({ title: 'Route THREE' }),
};
/**************/
/** Main **/
/**************/
export default class Main extends React.Component {
render() {
return (
<Navigator
style={{flex: 1}}
initialRouteStack={[
ROUTES.ONE(),
]}
renderScene={(route, navigator) =>
<Scene route={route} navigator={navigator} />
}
/>
);
}
}
/***************/
/** Scene **/
/***************/
class Scene extends React.Component {
state = {
menuIsOpen: false,
}
openMenu = () => {
this.setState({ menuIsOpen: true });
}
onMenuOpenChanged = (menuIsOpen) => {
if (this.state.menuIsOpen !== menuIsOpen) {
this.setState({ menuIsOpen });
}
}
render() {
const {route, navigator} = this.props;
return (
<View style={{flex: 1}}>
<SideMenu
menu={
<Menu
route={route}
navigator={navigator}
/>
}
menuPosition="right"
bounceBackOnOverdraw={false}
onChange={this.onMenuOpenChanged}
isOpen={this.state.menuIsOpen}
openMenuOffset={Dimensions.get('window').width * 0.75}
disableGestures={false}
>
<Screen route={route} navigator={navigator} openMenu={this.openMenu} menuIsOpen={this.state.menuIsOpen} />
</SideMenu>
</View>
);
}
}
/**************/
/** Menu **/
/**************/
class Menu extends React.Component {
render() {
const {route, navigator} = this.props;
return (
<View style={{ flex: 1, backgroundColor: 'coral', paddingTop: 25 }}>
<Text>Currently at {route.title}</Text>
<RouteMenuItem forRoute={ROUTES.ONE()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.TWO()} route={route} navigator={navigator} />
<RouteMenuItem forRoute={ROUTES.THREE()} route={route} navigator={navigator} />
</View>
);
}
}
const RouteMenuItem = ({forRoute, route, navigator}) => (
<TouchableHighlight onPress={() => navigator.push(forRoute)}>
<Text style={[ { padding: 15, borderColor: 'firebrick', borderWidth: 1 }, (forRoute.title === route.title ? {backgroundColor: 'green'} : {opacity: 0.5}) ]}>
Go to {forRoute.title} ({(forRoute.title === route.title ? 'current route' : 'NOT CURRENT ROUTE')})
</Text>
</TouchableHighlight>
);
/*****************************/
/** Screen, inside Menu **/
/*****************************/
class Screen extends React.Component {
render() {
const {route, navigator, openMenu, menuIsOpen} = this.props;
return (
<View style={{ flex: 1 }}>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', backgroundColor: 'peachpuff', paddingTop: 25 }}>
<HeaderButton onPress={navigator.pop}>Back</HeaderButton>
<HeaderButton onPress={openMenu}>Menu</HeaderButton>
</View>
<View style={{ flex: 1, backgroundColor: 'white' }}>
<Text style={{ margin: 50, fontSize: 50 }}>
{route.title}
</Text>
</View>
</View>
);
}
}
const HeaderButton = ({onPress, children}) => (
<TouchableHighlight underlayColor="green" onPress={onPress}>
<Text style={{ padding: 10, borderColor: 'firebrick', borderWidth: 1 }}>
{children}
</Text>
</TouchableHighlight>
);
The problem occurs becauase children of TouchableHighlight gets default opacity (1) after the TouchableHighlight is pressed. As that is a more specific problem I've asked a new question here.
In this case the bug appears when going back to a previous route, because the menu item in that instance of the Menu was pressed (moving us to a new route before we could spot the bug).