How to implement a Listview inside Modal in reactnative - reactjs

Amateur ReactNative developer: I am trying to implement a hardcoded data listview inside Modal of react native but unable to find any proper solution. And I don't want to use third party plugins.
Please help!
Here is my code.
export default class ActionSheetMenu extends React.PureComponent {
constructor(props){
super(props);
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(['row 1', 'row 2']),
};
}
state = {
visibleModal : false,
};
// data = [
// {
// "name": {
// "first": "aiden",
// "last": "lucas"
// },
// } ];
_renderButton = (text, onPress) => (
<TouchableOpacity onPress={onPress}>
<View>
<Text>{text}</Text>
</View>
</TouchableOpacity>
);
_renderModalContent = () => (
<View> style = {styles.modalContent}>
<Text>Hello</Text>
{this._renderButton('Close', () => this.setState({ visibleModal: false }) )}
</View>
);
render(){
return(
<View style={[styles.container,{backgroundColor: this.state.visibleModal ? 'rgba(0, 0, 0, 0.4)': 'white'}]}>
<Text>Text Behind Modal</Text>
{this._renderButton('BUTTON', () => this.setState({ visibleModal: true }))}
<Modal animationType="slide"
transparent
visible={this.state.visibleModal}>
<View style={styles.modalContent}>
<ListView
style={styles.listview}
dataSource = {this.state.dataSource}
renderRow={ (data) => <Text>{data}</Text> }
/>
<Text style={styles.textModal}>text inside modal</Text>
</View>
</Modal>
</View>
);
}
}

check your syntax for the below code:
<Modal
animationType={'slide'}
transparent={false}
visible={this.props.visibleModal}
onRequestClose={() => { this.changeVisibility(false); } }
>
Fly.. dont forget to change state value of visibleModal to false otherwise it wiil be rendered directly

Related

Calling function of class based component from another component in react native

I have two components one is Messages and other component is Navigation it's a stack navigator component. I would like to call function named onRefresh of Messages component from component Navigation header buttons. Please see my code how can I achieve this.
Messages.js (component file)
export default class Messages extends Component {
// Constructor
constructor(props) {
super(props)
this.state = {
messages: [],
isLoading: true
};
}
// End Constructor
// Getting Messages from server
async getMessages() {
try {
const response = await fetch('https://reactnative.dev/movies.json');
const json = await response.json();
this.setState({ messages: json.movies });
} catch (error) {
console.log(error);
} finally {
this.setState({ isLoading: false });
}
}
// End Getting messages from server
componentDidMount() {
this.getMessages();
}
// On refresh the messages
onRefresh = async () => {
this.setState({isLoading: true,}, () => {this.getMessages();});
}
// Send now sms using button.
sendNowMessage = async (title) => {
Alert.alert(title, "asdasdas");
}
render() {
const { messages, isLoading } = this.state;
return (
<SafeAreaView style={styles.container}>
{isLoading ? <ActivityIndicator size="large" color="#0000ff" style={ styles.horizontal } /> : (
<FlatList
data={ messages }
keyExtractor={({ id }, index) => id}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isLoading}
renderItem={({ item }) => (
<View style={styles.item}>
<Text style={{ marginBottom: 12, }}>{item.title}, {item.releaseYear}</Text>
<Button
onPress={() => this.sendNowMessage(item.title)}
title="Send SMS"
style={styles.sendSMS}
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
</View>
)}
/>
)}
</SafeAreaView>
);
}
}
Navigation.js (component file)
const Stack = createStackNavigator();
export default function Navigation() {
return (
<Stack.Navigator>
<Stack.Screen
name="AuthScreen"
component={AuthScreen}
options={{
title: "Welcome",
}}
/>
<Stack.Screen
name="Messages"
component={Messages}
options={{
headerTitle: 'Messages',
headerRight: () => {
return (
<View style={styles.headerButtons}>
<View style={{ marginRight:10, }}>
<TouchableOpacity
onPress={() => {
new Messages().onRefresh() <---- I am calling it like this.
}}
>
<Text
style={styles.actionButtons}
>
<Ionicons name="reload-sharp" size={20} color="black" />
</Text>
</TouchableOpacity>
</View>
<View>
<TouchableOpacity
onPress={() => {
alert("working")
}}
>
<Text
style={styles.actionButtons}
>
<Ionicons name="filter" size={20} color="black" />
</Text>
</TouchableOpacity>
</View>
</View>
)
}
}}
/>
</Stack.Navigator>
);
}
I am calling onRefresh function like this new Messages().onRefresh() and getting the following error.
Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the Messages component.
I want to learn what is the best way to call onRefresh function.

How do I make a generic handler for Toggle for setState in React Native

I want to make a generic handler for setstate rather than having two different handlers? How can I do that?
I am thinking of something like:
setStateHandler= (stateToBeChanged) =>{
this.setState({
stateToBeChanged: !this.state.stateToBeChanged
});
}
handleHappyToggle = () =>{
this.setState({
Happy: !this.state.Happy
});
}
handleSadToggle = () => {
this.setState({
Sad: !this.state.Sad
});
}
My two touchable opacities:
<TouchableOpacity
onPress={this.handleHappyToggle}
>
<Text> {this.state.happy ? 'Yes' : 'No' } </Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this.handleSadToggle}
>
<Text> {this.state.sad? 'Yes' : 'No' } </Text>
</TouchableOpacity>
EDIT: added demo StackSnippet...
EDIT 2: added 3 total ways to handle this "generically"
You could use a generic handler to accomplish this - just supply the "mood" as a parameter.
handleMoodToggle = mood => event => {
if(!["Happy", "Sad"].includes(mood)){
return null;
}
this.setState({
[mood]: !this.state[mood]
})
}
<TouchableOpacity onPress={this.handleMoodToggle("Happy")}>
<Text> {this.state.happy ? 'Yes' : 'No'} </Text>
</TouchableOpacity>
<TouchableOpacity onPress={this.handleMoodToggle("Sad")}>
<Text> {this.state.sad ? 'Yes' : 'No'} </Text>
</TouchableOpacity>
DEMO:
const { Component } = React;
const { render } = ReactDOM;
class App extends Component {
state = {
Happy: true,
Sad: false
};
handleMoodToggle1 = mood => event => {
if (!["Happy", "Sad"].includes(mood)) {
return null;
}
this.setState({
[mood]: !this.state[mood]
})
};
handleMoodToggle2 = event => {
this.setState({
[event.target.innerHTML]: !this.state[event.target.innerHTML]
});
}
handleMoodToggle3 = event => {
this.setState({
[event.target.name]: !this.state[event.target.name]
});
}
render() {
return (
<div>
<button onClick={this.handleMoodToggle1("Happy")}>Toggle Happy</button>
<button onClick={this.handleMoodToggle1("Sad")}>Toggle Sad</button>
<br />
<button onClick={this.handleMoodToggle2}>Happy</button>
<button onClick={this.handleMoodToggle2}>Sad</button>
<br />
<button name="Happy" onClick={this.handleMoodToggle3}>Happy 3</button>
<button name="Sad" onClick={this.handleMoodToggle3}>Sad 3</button>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>
);
}
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
you can check my exmaple for you:
export default class App extends Component {
state = {
emotions: [
{ name: 'happy', status: true },
{ name: 'sad', status: false },
{ name: 'angry', status: true },
]
}
handleEmotionToggle = (index) => {
let emotions = [...this.state.emotions]
emotions[index].status = !emotions[index].status
this.setState({
emotions
})
}
render() {
return (
<View style={styles.container}>
{
this.state.emotions.map((emotion, index) => {
return (
<TouchableOpacity key={index}
onPress={()=>this.handleEmotionToggle(index)}
>
<Text style={{margin:10,fontSize:20}}>{emotion.name} - {emotion.status ? "YES":"NO"} </Text>
</TouchableOpacity>
)
})
}
</View>
);
}
}

Access state in container component

How can I access (get and set) state inside a container component?
I am getting Undefined is not an object evaluating this.state.* because it cannot be accessed from MultiImagePost. Is there a way to bind this to the container component?
export default class ImagePost extends React.Component {
state = {
index: 0,
modalVisible: false,
currentImages: []
};
render() {
const { imageCount } = this.props;
if (imageCount == 1) {
return <SingleImagePost postObject={this.props} />
} else {
return <MultiImagePost postObject={this.props} />
}
}
const MultiImagePost = ({ postObject }) => (
<View>
<FlatList
data={[{key: 'a'}]}
renderItem={({item}) =>
<TouchableOpacity>
<View>
<FlatGrid
itemDimension={100}
items={postObject.images}
renderItem={({ item, index }) => (
<TouchableHighlight
onPress={() => {
this.setState({
modalVisible: true,
index: this.state.index,
currentImages: postObject.images
});
}}>
<Image
source={{ uri: item }}
/>
</TouchableHighlight>
)}
/>
</View>
</TouchableOpacity>
}
/>
</View>
);
}
MultiImagePost is a stateless(functional) component, so it has no state.
U should manage state in ImagePost or consider using React hook.
I can't run your code, if MultiImagePost is inside ImagePost, you may try to see if it works.
I think you have mixed up class component with functional component. You need to change your code a bit like,
export default class ImagePost extends React.Component {
state = {
index: 0,
modalVisible: false,
currentImages: []
};
render() {
const { imageCount } = this.props;
if (imageCount == 1) {
return <SingleImagePost postObject={this.props} />
} else {
return <>{this.MultiImagePost(this.props)}</> //Instead of component make it as function call
}
}
//Make this as instance function
MultiImagePost = (postObject) => (
<View>
<FlatList
data={[{key: 'a'}]}
renderItem={({item}) =>
<TouchableOpacity>
<View>
<FlatGrid
itemDimension={100}
items={postObject.images}
renderItem={({ item, index }) => (
<TouchableHighlight
onPress={() => {
this.setState({
modalVisible: true,
index: this.state.index,
currentImages: postObject.images
});
}}>
<Image source={{ uri: item }} />
</TouchableHighlight>
)}
/>
</View>
</TouchableOpacity>
}
/>
</View>
);
}

Slow image load/Pull images from storage - firebase - react native

I am pulling data from my firestore DB, one of the fields on each DB item is image, the value of each being a URL to an image with my firebase storage.
What I'd like to do is pull the image directly from storage as part of the loop that pulls from firestore.
I am trying to do this as when this loop renders the items, the images are taking a few seconds to appear on the screen.
Please advise if there is an alternative to solve this issue?
FirebaseData.js
onCollectionUpdate = (querySnapshot) => {
const data = [];
querySnapshot.forEach((doc) => {
const { title, image, url, description } = doc.data();
data.push({
key: doc.id,
doc, // DocumentSnapshot
title,
image,
description,
url
});
});
this.setState({
data,
loading: false
});
}
render() {
if (this.state.loading) {
return <Spinner />; //RETURN A LOADING ICON
}
return (
<List containerStyle={styles.listStyle}>
<FlatList
data={this.state.data}
renderItem={({ item }) => <ChildList {...item} />}
/>
</List>
);
}
}
ChildList.js
export default class ChildListRow2 extends React.PureComponent {
render() {
return (
<ListItem
title={
<View>
<Image source={{ uri: this.props.image }} style={styles.imageStyle} />
<Text style={styles.titleTextStyle}>{this.props.title}</Text>
</View>
}
subtitle={
<View>
<Text style={styles.subTitleTextStyle}>{this.props.description}</Text>
</View>
}
hideChevron
onPress={() => Linking.openURL(this.props.url)}
containerStyle={styles.listItemStyle}
/>
);
}
}
You can use onLoadEnd and onLoadStart properties of Image component to show and hide the rest of the data. You can show a loading spinner till the file loads and then show the complete component when it finishes.
Example
export default class ChildListRow2 extends React.PureComponent {
constructor() {
super();
this.state = {
loaded: false
};
}
imageLoaded = () => {
this.setState({ loaded: true })
}
render() {
return (
<ListItem
title={
<View>
<Image
source={{ uri: this.props.image }}
style={styles.imageStyle}
onLoadEnd={this.imageLoaded}
/>
<Text style={styles.titleTextStyle}>{this.props.title}</Text>
</View>
}
subtitle={
<View>
<Text style={styles.subTitleTextStyle}>{this.props.description}</Text>
</View>
}
hideChevron={this.state.loaded}
onPress={() => Linking.openURL(this.props.url)}
containerStyle={styles.listItemStyle}
/>
);
}
}

Trying to execute a function in a row of the ListView

i have a LisView in my component as follow:
class Test extends Component {
constructor(props) {
super(props);
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
dataSource: ds.cloneWithRows(this.props.dataSource),
};
}
componentWillReceiveProps(nextProps) {
if (this.props.dataSource !== nextProps.dataSource) {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(nextProps.dataSource),
});
}
}
onPressRow(key) {
console.log(key);
}
getListView() {
if (this.props.isLoading) {
return (
<ActivityIndicator
animating
size={'small'}
style={styles.spinner}
color={this.props.activityIndicatorColor}
/>
);
}
return (
<ListView
style={styles.listView}
keyboardShouldPersistTaps
keyboardDismissMode="on-drag"
enableEmptySections
dataSource={this.state.dataSource}
renderRow={this.renderRow}
automaticallyAdjustContentInsets={false}
/>
);
}
renderRow(rowData) {
return (
<View
style={styles.listButton}
>
<TouchableHighlight
style={styles.button}
onPress={() => this.onPressRow(rowData)}
>
<Text>
{rowData.description}
</Text>
</TouchableHighlight>
</View>
);
}
render() {
return (
<View>
<View>
// TEXTINPUT
</View>
{this.getListView()}
</View>
);
}
}
export default Test;
When i click on a row i want execute a function but i have this error:
_this2.onPressRow is not a function
Why i can't to read the function? i must pass it as a props?
If you use React.createClass, all calls are automatically binded, but when using the ES6 approach of extends React.Component you have to bind all your functions.
You can do it either inline using
renderRow={this.renderRow.bind(this)}
Alternatively, you can add to your constructor, after super():
this.renderRow = this.renderRow.bind(this);
Personally I like this approach as I find this code a bit easier to read.
For more information about the ES6 way, check this link.

Resources