Using function enum to render components in React-native app - react-native

I am following this link to render components using enum instead of several if-else conditions. My code looks like-
render(){
const _renderHelper = (scene, index) => ({
'ComponentType1': <Text>Test</Text>,
'ComponentType2': <Text>Test</Text>,
});
return (
<View>
{
this.props.scene.components.map((scene, sceneIndex) => {
_renderHelper(scene, sceneIndex)[scene.componentType]
})
}
</View>
)
But nothing renders in the app.

Related

Navigate from a function call React native

Hi guys I am having trouble finding a solution on my problem because I want to navigate to another page but here's the problem
function NewOrderPopUp({id, services, name, rating, accepted, destinationPlaceName, userPlaceName, driverName, driverContactNumber, driverRating, driverTrackingNumber})
{
async function toggleAcceptBooking()
{
await firestore()
.collection('userBookingRequest')
.doc(id)
.update({
accepted : !accepted,
driverName: 'Sample Driver',
driverContactNumber: '09123456789',
driverRating: '4.9',
driverPlateNumber: 'NFT-2020',
driverTrackingNumber: GenerateTrackingNumber(),
})
CheckIfBookingAccepted();
}
}
return (
<View>....</View>
);
export default NewOrderPopUp;
And I am calling the NewOrderPopUp Page in another file.
like this
import NewOrderPopUp from "../../components/NewOrderPopUp";
const HomeScreen = () => {
//... codes here
return (
<View>
<FlatList
horizontal
contentContainerStyle={{paddingHorizontal: 30}}
data={userBookingData}
keyExtractor={(item) => item.id}
renderItem={({item}) => <NewOrderPopUp {...item}/>} />
</View>
);
}
export default HomeScreen;
What I wanted is that if I click the toggleAcceptBooking it will nagivate to another page like
navigation.navigate('BookingPage');
Can someone enlighten me please . Thank you.
Do it by passing navigation down as a prop.
do the following steps.
handle navigation prop in HomeScreen
const HomeScreen = ({ navigation }) => {
}
pass navigation as a prop to NewOrderPopUp
<FlatList
...
renderItem={({item}) => <NewOrderPopUp navigation={navigation} {...item}/>} />
handle navigation prop in NewOrderPopUp and use it to navigate.
function NewOrderPopUp( {navigation, ...} ){
async function toggleAcceptBooking(){
await ...
navigation.navigate('BookingPage');
}
}

Iterate over values of Map to render all icon components but don't work however render one icon works

I am developing react-native project.
I have a function which set icon metadata into a Map :
export function getIconsMetadata() {
// a map of icons' metadata
const iconsMetadata = new Map();
...
// code to set icon metadata to the map
iconsMetadata.set("foo", "Foo");
iconsMetadata.set("bar", "Bar");
...
return iconsMetadata;
}
There is another function which returns the actual icon component based on the icon type (i.e. the value of iconsMetadata holds the icon type):
export function getMyIcon(iconType) {
switch (iconType) {
case 'Foo':
return <Foo />;
case 'Bar':
return <Bar />;
...
}
In my screen, I have a function to show icon component by iterating over the values of the above icons' metadata Map, and try to render each icon component:
export const MyScreen() => {
const showIcons = () => {
[...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>
{iconComponent}
</View>;
});
};
return (
<View style={styles.container}>
{/*I call the showIcons function here to render icons*/}
{showIcons()}
</View>
)
}
Problem is the icons are not shown on screen.
But if I directly return one icon component in my screen:
export const MyScreen = () => {
...
const showOneIcon = () => {
return <View>
<Foo />
</View>;
});
}
return (
<View style={styles.container}>
{/*I show one icon*/}
{showOneIcon()}
</View>
)
}
The <Foo /> icon component is rendered successfully on the screen.
So, why iterating the map to show all icons don't work?
The problem is that you’re not returning anything from showIcons. Either you remove { } from there
const showIcons = () =>
[...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>{iconComponent}</View>;
});
or add return before [...getIconsMetadata().values()].map
const showIcons = () => {
return [...getIconsMetadata().values()].map((iconType, index) => {
const iconComponent = getMyIcon(iconType);
return <View key={index}>{iconComponent}</View>;
});
};

Warning when switching to another screen if the user has authorization

When I open the application and if the isAuth variable is true, it means the user has authorization, then I want to go to the provider screen.
My authorization component.
export const LoginScreen: NavigationStackScreenComponent<NavigationParams> = observer(({ navigation }: NavigationParams) => {
useEffect(() => {
AuthState.checkAuthentication();
}, []);
if (AuthState.isAuth) {
navigation.navigate('Provider');
}
return <View style={styles.body}>{LoaderState.loading ? <LoaderComponent /> : <AuthComponent />}</View>;
});
My provider component.
export const ProvidersScreen: NavigationStackScreenComponent<NavigationParams> = observer(({ navigation }: NavigationParams) => {
useEffect(() => {
ProvidersState.setProviders();
}, []);
return (
<View style={styles.body}>
{LoaderState.loading ? (
<LoaderComponent />
) : (
<ItemListComponent itemsList={ProvidersState.providersList} />
)}
</View>
);
});
But I get a warning. I understand that it is associated with the react-navigation library.
How to switch to another screen using a conditional statement?
Functional components are basically the equivalent to the render() function in class components. You are calling navigate before the component gets render and that's a problem.
You can either call the navigate inside your useEffect or put the conditional logic in the parent view.
In the first case, navigate will be called AFTER the component renders
useEffect(() => {
AuthState.checkAuthentication();
if (AuthState.isAuth) {
navigation.navigate('Provider');
}
}, []);
so it will be visible for half a second and then change. If you'd like to avoid this, then go for the second option.
You can put the condition in the parent. Something like
<View>{AuthState.isAuth ? <ProvidersScreen /> : <LoginScreen />}</View>

Flat List not re-rendering even when I change a state

Expo-cli: 2.2.0
React-Navigation: 2.18.0
I have the following two screens for React Navigation, where one is to input the form details and another is the screen where the user can either edit on the submissions or confirm.
My Input interface looks like this:
export default class PickDropInterface extends
React.Component<NavigationProps<>> {
this.state = { tasks: [],
}
onSubmit = () => { this.props.navigation.navigate("Confirmation",
{tasks: this.state.tasks, deleteItem: this.deleteItem.bind(this)}); }
deleteItem = (key) => { var filteredTasks =
this.state.tasks.filter(function (item) { return (item.key !==key);
});
render() {
return (
<ItemDetail onSubmit={this.onSubmit} /> ) }
My Confirmation screen looks like this:
export default class Confirmation extends React.Component<NavigationProps<>> {
this.state={
refresh: false,
}
deleteItem = (key) => {
this.props.navigation.state.params.deleteItem(key);
this.setState({
refresh: !this.state.refresh
})
}
_renderItem =({ item }) =>
(
<View style={styles.cardStyle}>
<Button
primary
label="Delete" onPress= {() => {this.deleteItem(item.key)}} /></View>
)
render() {
return (
<FlatList data={task}
renderItem= {this._renderItem}
keyExtractor= {(item) => item.key.toString()}
extraData={this.state} />
)
}
Expected Output:
The delete button to prompt refresh in the FlatList and show the new Task list.
Current Output:
https://www.youtube.com/watch?v=RmrurTBQpak&feature=youtu.be
I don't know why FlatList didn't re-render, but I found a much simpler solution to what I wanted.
I used conditional rendering instead and I kind of think it's the way to do it instead of navigating to the other screen.
What I did is:
I made a new state called 'orderComplete' and set it to false as default.
Whenever, it is 'false', I made inputInterface above to render whereas it was 'true', I made the above ConfirmationScreen render.
More on Conditional Rendering can be found in React's official documentation.
FlatList above works like a charm now.

How to read props from a React Native touchable event currentTarget?

I have the following React Native code that runs the press() method when a user taps an image. I want to get the itemIndex prop from the event object. I set a break point in the press method and added some expressions to the Watch. From the Watch I determined that the target (event origination) from the event is the Image which is correct. The itemIndex prop is also available. The element being processed is the currentTarget, the Watch sees it's a "RCTView" and I was expecting a TouchableOpacity, so maybe underneath TouchableOpacity is a View? The currentTarget itemIndex prop is undefined, why? How can I get the props from the currentTarget?
I want to do it this way to avoid creating addition methods for each rendered item.
FYI,
ref={(c) => this._input = c} will not work because it's being run in a loop.
onPress={(e) => this.press(e, i)} creates a new function which I'm trying to avoid.
Watch
target._currentElement.props.itemIndex: 2
target._currentElement.type.displayName: "RCTImageView"
currentTarget._currentElement.props.itemIndex: undefined
currentTarget._currentElement.type.displayName: "RCTView"
press: function(event){
var currentTarget = ReactNativeComponentTree.getInstanceFromNode(event.currentTarget);
var target = ReactNativeComponentTree.getInstanceFromNode(event.target);
var currentTargetIndex = currentTarget._currentElement.props.itemIndex;
var targetIndex = target._currentElement.props.itemIndex;
var url = this.state.data.items[currentTargetIndex].url;
Linking.openURL(url).catch(err => console.error('An error occurred', err));
},
render: function() {
return (
<ScrollView horizontal={true} showsHorizontalScrollIndicator={false} style={styles.galleryView}>
{
this.state.data.items.map((data, i) =>
<TouchableOpacity itemIndex={i} key={i} activeOpacity={0.5} onPress={this.press} >
<Image itemIndex={i} key={i} source={{uri:data.previewImageUri}} style={styles.galleryImage} />
</TouchableOpacity>
)
}
</ScrollView>
);
}
I actually came across this same issue recently, I found two different ways you could approach this. The easier way of doing it is altering your onPress to pass an index to your press function, this is the 2nd way of doing it:
press: function(event, index){
var url = this.state.data.items[index].url;
Linking.openURL(url).catch(err => console.error('An error occurred', err));
},
render: function() {
return (
<ScrollView
horizontal={true}
showsHorizontalScrollIndicator={false}
style={styles.galleryView}
>
{
this.state.data.items.map((data, i) =>
<Images data={data} key={i} index={i} press={this.press} />
)
}
</ScrollView>
);
}
const Images = (props) => {
const imageClicked = (e) => {
props.press(e, props.index);
}
return (
<TouchableOpacity activeOpacity={0.5} onPress={imageClicked} >
<Image source={{uri:props.data.previewImageUri}} style={styles.galleryImage} />
</TouchableOpacity>
)
}
You could make your event handler a curried function that accepts extra parameters.
//Curried function for onPress event handler
handleOnPress = (someId, someProp) => event => {
//USE someProp ABOVE TO ACCESS PASSED PROP, WHICH WOULD BE undefined IN THIS CASE
//Use event parameter above to access event object if you need
console.log(someProp)
this.setState({
touchedId: someId
})
}
Checkout the working snack below
https://snack.expo.io/#prashand/accessing-props-from-react-native-touch-event
Binding the needed information to a callback and assigning one to each child avoids recreating the callback on every render of children.
class Hello extends React.Component{
state = { names: this.props.names.map((name, i) => {
return Object.assign({
onClick: this._onClick.bind(this, i, this.props),
}, name)
}),
};
_onClick(ind, _props, e) {
alert('props:' + JSON.stringify(_props));
}
render() {
const { names } = this.state;
return (
<div>
{ names.map((name, i) => (
<div key={i}>Name: <input value={ name.first } onClick={ name.onClick } /></div>
))}
</div>
)}
}
ReactDOM.render(
<Hello names={[{first:'aaa'},{first:'bbb'},{first:'ccc'}]}/>,
document.getElementById('container')
);
JS Fiddle