I am new one in React Native. I have to make an app with google map and make possible to change map type from other screen. I am using TabNavigator to switch between screens.
Qestion is how to change map type by switching a radio button in other screen.
This is my HomeScreen map component:
<View style={styles.container}>
<MapView
mapType={'standard'}
provider={this.props.provider}
style={styles.map}
initialRegion={this.state.region}
onPress={e => this.onPress(e)}
>
I want to pass a mapType from another screen with radio buttons
const MAP_TYPES = {
STANDARD: 'standard',
SATELLITE: 'satellite',
HYBRID: 'hybrid',
TERRAIN: 'terrain',
NONE: 'none',};
var radio_props = [
{label: MAP_TYPES.STANDARD, value: MAP_TYPES.STANDARD },
{label: MAP_TYPES.SATELLITE, value: MAP_TYPES.SATELLITE }];
export default class LinksScreen extends React.Component {
static navigationOptions = {
title: 'Options',
};
onPress(value) {
this.setState({value:value});
/*
Here I want to send value to HomeScreen but I don't know how =(
*/
console.log(value);
}
render() {
return (
<ScrollView style={styles.container}>
<RadioForm
radio_props={radio_props}
initial={0}
onPress={(value) => {this.onPress(value)}}
/>
</ScrollView>
);
}
}
App Images
This is my home screen with map
This is my switch
There are two common techniques for this.
Use a higher order component to maintain the state of the map type, and pass it down to child components as a prop. Also pass down a function which can be used to update the state in the higher order component.
Use a system such as redux to create a single state which you can read and update from anywhere in your application.
It’s a matter of personal preference which method you choose, though if the app is going to get more complex over time, some global state management such as redux may be a good idea to use early on.
Related
For the case of this question let's say I'm building Medium as a React Native app, specifically the screen where you read a story.
This is a long screen, with different types of content appearing as you scroll down. The title, the author information, the content (paragraphs, images, videos, etc), some meta information at the end, social features like comments, etc.
What's the recommended way to build a view like this? ScrollView doesn't seem performant enough especially if there were videos or other media types that needed to be loaded within the content. ListViews seem like they are the more performant option but don't seem like they are designed for this use-case.
Has anyone else faced this challenge? What's the best way to approach it?
<ScrollView> renders all its children first. So, let say you have thousands of elements(medium articles), <ScrollView> will first render all of them, which is pretty much slow and is visible.
For these cases, you can use <FlatList>, which renders the child when it enters the viewport. So, it is performant.
You can refer <FlatList> documentation here: https://facebook.github.io/react-native/docs/flatlist
I had a similar problem for one screen in my app where there were various categories of components involved, for my case <FlatList> worked very well, performance was also up-to the mark.
Solution
Example.js
import React, { Component } from "react";
import { FlatList } from "react-native";
export default class Example extends Component {
constructor(props) {
super(props);
this.state = {
showProgress: false,
content: [{}, {}, {}] // list of objects
};
}
onRefresh() {
this.setState({ showProgress: true });
// call methods here to load new data, once loaded make sure to reset the loading state to false
this.setState({ showProgress: false });
}
showParagraph() {
return <React.Fragment>{/* Your paragraph */}</React.Fragment>;
}
showVideo() {
return <React.Fragment>{/* Your Videos */}</React.Fragment>;
}
showAudio() {
return <React.Fragment>{/* Your Audio */}</React.Fragment>;
}
renderItem(item) {
return (
<React.Fragment>
{item.isVideo ? showVideo() : showParagraph()}
</React.Fragment>
);
}
NoDataMessage() {
return <React.Fragment>{/* Empty screen message goes here */}</React.Fragment>;
}
render() {
return (
<FlatList
data={this.state.content}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item }) => this.renderItem(item)}
onRefresh={() => this.onRefresh()}
refreshing={this.state.showProgress}
ListEmptyComponent={this.NoDataMessage()}
/>
);
}
}
read more about props from here, also you can make an infinite news feed like facebook home page check this React native infinite scroll with flatlist
hope this helps..!
I'm interested in having a view which initially loads with my React Native app that essentially has nested components in it. These components will give visual queues to the user as to what state the app is in, eg: still loading data from the server, etc. Basically, it's not just a static splash screen. I might also add some kind of spinner/progress bar, eg: other animated components.
There are solutions out there for static splash screens that initially show while your app loads into memory, but I need to be able to load an initial component, and then remove it when the application's data is ready to go. Is there a convention/pattern to follow in order to achieve this? Is there a way to mount a component, then remove it when it's no longer necessary allowing the rest of the app to be displayed? What's a best practice for this using React Native?
This is what I used to do:
Use <Modal /> to provide your initial, interactive page. It will blocks the screen, with semi-transparent background; If you like it to be full width, just use flex: 1 within the <View /> inside <Modal />.
Use global object / queue for loading status information. My choice is rxjs, then your initial page can just listen to this one source of truth, suggest a BehaviorSubject. So you can subscribe on it for something like:
...
{ tag: 'FetchRemoteData', progress: 10 }
{ tag: 'LoadingComponent', progress: 5 }
{ tag: 'FetchRemoteData', progress: 20 }
...
Read it until match your "load complete" conditions, then close the model.
To make it clear with code.
app.js
render() {
return (
<View>
<InitialBlockingPage />
<YourMainApp />
</View>
);
}
initial-blocking-page.js
constructor(props) {
super(props);
this.state = {
visible: true
};
}
componentDidMount() {
globalQueue.subscribe( () => {
/// pseudo code: until fully loaded
if (fullloaded) this.setState({visible: false});
});
}
render() {
return (
<Modal visible={this.state.visible}>
<SplashScreenWithData />
</Modal>
);
}
I'm trying to call a function that will fire upon onFoucs on TextInput that will scroll the scrollView all the way down (using scrollToEnd())
so this is my class component
class MyCMP extends Component {
constructor(props) {
super(props);
this.onInputFocus = this.onInputFocus.bind(this);
}
onInputFocus() {
setTimeout(() => {
this.refs.scroll.scrollToEnd();
console.log('done scrolling');
}, 1);
}
render() {
return (
<View>
<ScrollView ref="scroll">
{ /* items */ }
</ScrollView>
<TextInput onFocus={this.onInputFocus} />
</View>
);
}
}
export default MyCMP;
the component above works and it does scroll but it takes a lot of time ... I'm using setTimeout because without it its just going down the screen without calculating the keybaord's height so it not scrolling down enough, even when I keep typing (and triggering that focus on the input) it still doesn't scroll all the way down.
I'm dealing with it some good hours now, I did set the windowSoftInputMode to adjustResize and I did went through some modules like react-native-keyboard-aware-scroll-view or react-native-auto-scroll but none of them really does the work as I need it.
any direction how to make it done the right way would be really appreciated. thanks!
Rather than using a setTimeout you use Keyboard API of react-native. You add an event listener for keyboard show and then scroll the view to end. You might need to create some logic on which input is focused if you have more than one input in your component but if you only have one you can just do it like the example below.
Another good thing to do is changing your refs to functional ones since string refs are considered as legacy and will be removed in future releases of react. More info here.
class MyCMP extends Component {
constructor(props) {
super(props);
this.scroll = null;
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
}
componentWillUnmount () {
this.keyboardDidShowListener.remove();
}
_keyboardDidShow() {
this.scroll.scrollToEnd();
}
render() {
return (
<View>
<ScrollView ref={(scroll) => {this.scroll = scroll;}}>
{ /* items */ }
</ScrollView>
<TextInput />
</View>
);
}
}
export default MyCMP;
If you have a large dataset React Native docs is telling you to go with FlatList.
To get it to scroll to bottom this is what worked for me
<FlatList
ref={ref => (this.scrollView = ref)}
onContentSizeChange={() => {
this.scrollView.scrollToEnd({ animated: true, index: -1 }, 200);
}}
/>
I'd like to have a context menu triggered on long press different places using React Native.
I.e. in a dialer like the default dailer. You can long-click on any contact and get a 'copy number' menu. And also you can long-click on the name of the person once you've opened their 'contact card'.
The straight-forward way needs a lot of copy-pasted boilerplate, both components and handlers.
Is there a better pattern for doing this?
All Touchable components (TouchableWithoutFeedback, TouchableOpacity etc.) has a property called onLongPress. You can use this prop to listen for long presses and then show the context menu.
To eliminate code mess and doing lots of copy paste you can separate your context menu as a different component and call it when the long press happen. You can also use an ActionSheet library to show the desired options. React native has a native API for iOS called ActionSheetIOS. If you get a little bit more experience in react and react-native you can create a better logic for this but I'm going to try to give you an example below.
// file/that/contains/globally/used/functions.js
const openContextMenu = (event, user, callback) => {
ActionSheetIOS.showActionSheetWithOptions({
options: ['Copy Username', 'Call User', 'Add to favorites', 'Cancel'],
cancelButtonIndex: [3],
title: 'Hey',
message : 'What do you want to do now?'
}, (buttonIndexThatSelected) => {
// Do something with result
if(callback && typeof callback === 'function') callback();
});
};
export openContextMenu;
import { openContextMenu } from './file/that/contains/globally/used/functions';
export default class UserCard extends React.Component {
render() {
const { userObject } = this.props;
return(
<TouchableWithoutFeedback onLongPress={(event) => openContextMenu(event, userObject, () => console.log('Done')}>
<TouchableWithoutFeedback onLongPress={(event) => openContextMenu(event, userObject, () => console.log('Done'))}>
<Text>{userObject.name}</Text>
<Image source={{uri: userObject.profilePic }} />
</TouchableWithoutFeedback>
</TouchableWithoutFeedback>
);
}
}
Similarly as the previous answer combine onLongPress with imperative control for popup menu - something like
<TouchableWithoutFeedback onLongPress={()=>this.menu.open()}>
<View style={styles.card}>
<Text>My first contact name</Text>
<Menu ref={c => (this.menu = c)}>
<MenuTrigger text="..." />
<MenuOptions>
// ...
</MenuOptions>
</Menu>
</View>
</TouchableWithoutFeedback>
When it comes to a lot of boilerplate - in React you can do your own components that you can reuse everywhere thus reducing boilerplate (and copy&paste)
See full example on https://snack.expo.io/rJ5LBM-TZ
I'm trying to create a small app, but have many questions)
I have 3 screen and use react-navigation to pass data. the scheme is following:
1) loading screen (fetch data and save it to AsyncStorage+handling data and save to obj1 for pickers)
(pass obj1)
2)main screen (get data,render pickers based on it, take selected values and pass them next)
(pass pickers selection+input)
3)some results(get data from Asyncstorage, some calculating and render results)
so I have two questions.
when I navigate back from 3) to 2) I have an error that screen2 need data, which was passed from screen1. yes - i've checked if this data pass to 3 and then to 2 when Back Button is pressed, and there is no error, but I'm sure this is bad solution
and second..trying to explain) on screen 3 some calculations made on pickers selection, so it hasn't problem. but rest of them needed get data from AsyncStorage and then convert it according to Picker values and render to ListView. Despite I'm putting getting from AS on componentWillMount it's still take much time so data for rendering is undefined. Of course I'm using states, but I think this is a bad logic of data handling..
UPD
so I'm trying pass data from child(screen) to parent(index.ios.js), where it define as first loading view( I'm using navigator's stack screens)
export default class App extends Component {
constructor(props) {
super(props);
};
}
myCallback(dataFromChild) {
console.log("yeah", dataFromChild);
}
render() {
return (
<LoadingScreen callbackFromParent={this.myCallback}/>
);
}
}
and LoadingScreen.js:
render() {
return(
<View style={{flex: 1, backgroundColor: '#dc143c', marginTop:20}}>
<Image
source={require('./WhiteLogo.png')}
style={{flex:3, height: undefined, width: undefined, marginLeft:20, marginRight:20}}
resizeMode="contain"
qwer={this.props.callbackFromParent('listInfo').bind(this)}
/>
<Spinner color='white' style={{flex:1}}/>
</View>
);
}
}
and I've got an error "Unhandled JS Exception: this.props.callbackFromParent is not a function"
AsyncStorage might not be the best solution for what you are trying. Using react-navigation for data delivery is not the best neither. I would suggest checking redux for storing states globally and delivering it to the pages when needed. There is also another way which is passing functions to the props of child components for pulling up any data from child to parent components. You can check this and this or the sample code is below.
Parent
class Parent extends Component {
updateState (data) {
this.setState(data);
}
render() {
<View>
<Child updateParentState={this.updateState.bind(this)} />
</View>
}
}
Child
class Child extends Component {
render() {
<View>
<Button title="Change" onPress={() => {this.props.updateParentState({name: 'test})}} />
</View>
}
}