A problem with a trivial react native code with hooks - react-native

Update:
Problem 2. is solved (thanks to #NikolayNovikov reply) by replacing "setResources({ resources: response.data })" with
"setResources(response.data)".
Problem 1. I cannot reproduce it...
Description of the problem:
The following trivial example is fetching data from jsonplaceholder.typicode.com, and, since there are 100 'posts' and 200 'todos', it is supposed to display 100 and 200 when you click on these buttons.
For some reason that I cannot find (embarrassing, I know...), there are two problems:
When I click on the 'posts' button, useEffect is called twice (once with 'todos' and once with 'posts', and when I click on the 'todos' button it is not called at all.
resource.length is not rendered
Any idea what is wrong?
App.js:
import React, { useState } from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { ResourceList } from './components/ResourceList';
console.warn('In App.js: Disabling yellowbox warning in genymotion');
console.disableYellowBox = true;
export const App = () => {
const [resource, setResource] = useState('todos');
return (
<View style={{ flex: 1, alignItems: 'center', marginTop: 100 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}>
The Application has been loaded!
</Text>
<View style={{ flexDirection: 'row', marginTop: 100,
alignItems: 'center', justifyContent: 'center' }}>
<TouchableOpacity onPress={() => setResource('posts')}>
<View style={styles.button}>
<Text style={styles.buttonText}>
Posts
</Text>
</View>
</TouchableOpacity>
<View style={{ width: 20 }} />
<TouchableOpacity onPress={() => setResource('todos')}>
<View style={styles.button}>
<Text style={styles.buttonText}>
Todos
</Text>
</View>
</TouchableOpacity>
</View>
<ResourceList resource={resource} />
</View>
);
}
const styles = StyleSheet.create({
buttonText: {
color: 'black',
fontSize: 20
},
button: {
backgroundColor: '#a8a',
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 2
}
});
ResourceList.js:
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import axios from 'axios';
// export const ResourceList = (props) => {
export const ResourceList = ({ resource }) => { // restructured props
const [resources, setResources ] = useState([]);
const fetchResource = async(resource) => {
console.log('+++ ResourceList/fetchResource calling axios. path:',
`https://jsonplaceholder.typicode.com/${resource}`);
const response = await axios.get(`https://jsonplaceholder.typicode.com/${resource}`);
console.log(response);
setResources({ resources: response.data })
}
useEffect(() => {
console.log('--- ResourceList/useEffect. resource: ', resource);
fetchResource(resource);
}, [resource]);
return (
<View style={{ flex: 1, alignItems: 'center', marginTop: 100 }}>
<Text style={{ fontSize: 20, fontWeight: '500' }}>
{resources.length}
</Text>
</View>
);
}

In you ResourceList.js component setResources should be different, please try this:
setResources(response.data)

Related

React-native I loop through it, but nothing comes out of the children

I tried to show images and people's names using loops.
import { StyleSheet, Text, View, SafeAreaView, ScrollView } from 'react-native'
import React from 'react'
import Header from './home/Header'
import Stories from './home/Stories'
import Posts from './home/Posts'
import { POSTS } from '../data/posts'
const HomeScreen = () => {
return (
<SafeAreaView style={styles.container}>
<Header></Header>
<ScrollView>
<Stories></Stories>
{POSTS.map((post, index) => (
<Posts post={post} key={index}/>
))}
</ScrollView>
</SafeAreaView>
)
}
export default HomeScreen
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black'
},
});
This is the one that has a problem.
import { Image, StyleSheet, Text, View } from 'react-native'
import {Divider} from 'react-native-elements'
import React from 'react'
const Posts = ({post, index}) => {
return (
<View stlye={styles.conatiner}>
<Divider width={1} orientation='vertical'></Divider>
<PostHeader post={post} key={index}/> <--- this part not show up
</View>
)
};
const PostHeader = ({post, index}) => {
<View style={{flexDirection: 'row', justifyContent: 'space-between', margin: 5, alignContent: 'center'}} key={index}>
<View>
<Image source={{ uri: post.profile_pictrue}} style={styles.story}/>
<Text style={{color: 'white'}}>{post.user.user}</Text>
</View>
</View>
};
const styles = StyleSheet.create({
conatiner: {
marginBottom: 30
},
story: {
width: 35,
height: 35,
borderRadius: 50,
marginBottom: 10,
borderWidth: 2,
borderColor: 'orange'
},
});
export default Posts
But the divider is working. And, there is no error log.
I don't understand what have I done wrong.
you need to wrap with return
const PostHeader = ({post, index}) => {
return( <View style={{flexDirection: 'row', justifyContent: 'space-between', margin: 5, alignContent: 'center'}} key={index}>
<View>
<Image source={{ uri: post.profile_pictrue}} style={styles.story}/>
<Text style={{color: 'white'}}>{post.user.user}</Text>
</View>
</View>)
};
hope it's working fine

useState array not updating correctly as it creates duplicate items

I am trying to update the useState() array by passing through value of TextInput (onChangeText). The code is working fine however whenever I am clinking onPress (addBook function) it is updating/ adding previous text in the array not current one. So, to add current text I have to click button twice which is creating dupliate items in my array.
I may be making very studpid misatke but cant find it. Can any one help. Below is my entire code.
import React, { useState } from "react";
import { TextInput, View, Dimensions, TouchableOpacity } from "react-native";
import { MaterialCommunityIcons } from "#expo/vector-icons";
import { Entypo } from "#expo/vector-icons";
const screenHeight = Math.round(Dimensions.get("window").height);
const AddBookName = ({ hidePressed }) => {
const [book, setBook] = useState();
const [bookList, setBookList] = useState(["no name"]);
const addBook = () => {
setBookList([...bookList, book]);
console.log(bookList);
};
return (
<View style={{ flexDirection: "row" }}>
<View style={{ flex: 1 }}>
<TextInput
style={{
fontSize: 20,
paddingVertical: 10,
backgroundColor: "#ececec",
paddingLeft: 10,
}}
placeholder="Enter the Book Name"
placeholderTextColor="grey"
// value={book}
onChangeText={(text) => setBook(text)}
type="text"
></TextInput>
</View>
<TouchableOpacity
style={{
flex: 0.15,
backgroundColor: "green",
alignItems: "center",
justifyContent: "center",
}}
onPress={() => addBook()}
>
<View>
<MaterialCommunityIcons name="check" size={24} color="white" />
</View>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 0.15,
backgroundColor: "red",
alignItems: "center",
justifyContent: "center",
}}
onPress={hidePressed}
>
<View>
<Entypo name="cross" size={24} color="white" />
</View>
</TouchableOpacity>
</View>
);
};
export default AddBookName;
Try to clean the previous input value after adding to the list.
const addBook = () => {
if (book) {
setBookList([...bookList, book]);
console.log(bookList);
setBook("");
}
};
This should remove duplicates:
const addBook = () => {
setBookList(Array.from(new Set([...bookList, book])));
console.log(bookList);
};
You could also store your booklist as a set instead of an array.

Sending data to another component with (React Native Navigation)

I want to send basic data from Home.js component to details.js component.
My home.js
import React, { useState, useEffect } from 'react'
import { View, TouchableHighlight, Image, FlatList, ActivityIndicator, Text, StyleSheet } from 'react-native'
function HomeScreen({ navigation }) {
const [isLoading, setIsLoading] = useState(true)
const [dataSource, setDataSource] = useState([])
useEffect(() => {
fetch('http://www.json-generator.com/api/json/get/bVxGaxSSgi?indent=2')
.then((response) => response.json())
.then((responseJson) => {
setIsLoading(false)
setDataSource(responseJson)
})
.catch((error) => {
alert(error)
});
})
if (isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
)
}
return (
<View style={{ flex: 1, paddingTop: 20, paddingHorizontal: 20 }}>
<FlatList
data={dataSource}
renderItem={({ item }) =>
<TouchableHighlight onPress={() => navigation.navigate('Details',{text:'alperen'})} underlayColor="white">
<View style={styles.button}>
<View style={styles.div}>
<View style={styles.inlineDiv}>
<Image source={{ uri: 'https://source.unsplash.com/random/900&#x2715700/?fruit' }} style={styles.pic} />
<Text>{item.name}, {item.about}</Text>
</View>
</View>
</View>
</TouchableHighlight>
}
keyExtractor={({ _id }, index) => _id}
/>
</View>
)
}
export default HomeScreen
const styles = StyleSheet.create({
pic: {
width: '100%',
height: 200,
borderRadius: 20,
},
div: {
shadowColor: "#fff",
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.20,
shadowRadius: 50,
elevation: 0,
marginBottom: 10
},
inlineDiv: {
padding: 5,
},
button: {
alignItems: 'center',
},
});
This is my details.js component
import React, { useEffect } from 'react'
import { View, Text } from 'react-native'
function DetailsScreen( navigation ) {
useEffect(() => {
alert(navigation.text)
})
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text></Text>
</View>
);
}
export default DetailsScreen
I want to go to the other page and at the same time I want to send and print the data. This code is currently returning undefined. How Can I send data ? Probably I can use props but I don't know usage.
Change
function DetailsScreen( navigation ) {
useEffect(() => {
alert(navigation.text)
})
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text></Text>
</View>
);
}
to
function DetailsScreen({navigation}) {
useEffect(() => {
alert(navigation.state.params.text)
})
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text></Text>
</View>
);
}
And try
In first screen
<TouchableHighlight onPress={() => navigation.navigate('Details',{item})} underlayColor="white">
In next screen you can get data
this.props.navigation.state.params.item
Example:
<Image source={this.props.navigation.state.params.item.img}/>
<Text>{this.props.navigation.state.params.item.text}</Text>
Sending data operation is success but details page is not working. I looked 'react-navigation documentation ' (https://reactnavigation.org/docs/en/params.html). There is a keyword ('route') we have to use it.
function DetailsScreen({ route }) {
const { text } = route.params;
useEffect(() => {
var a = JSON.stringify(text)
alert(a)
})
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text></Text>
</View>
);
}
I tried this code and its working success.

Creation of a new component with navigation fields

I try to create a new react-native component that contains two button. I define two property next to going on the next page and prev to going in the precedent page but I have this error:
undefined is not an object (evaluating '_this.props.navigation.navigate')
I found so much question similar this but I don't found any valid solution.
This is the code:
import PropTypes from 'prop-types'
import React, { Component } from 'react';
import { View, StyleSheet, Button } from 'react-native';
export default class ButtonNavigation extends Component {
static propTypes = {
next: PropTypes.string,
prev: PropTypes.string,
}
render() {
const { next, prev } = this.props;
return (
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(next)} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.navigation.navigate(prev)} title='Avanti' color='gold'/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
bottomContainer: {
flex: 1,
flexDirection: 'row',
position: 'absolute',
bottom: 10,
padding: 20,
justifyContent: 'space-between'
},
text: {
fontSize: 30,
},
buttonContainer: {
flex: 1,
padding: 20,
justifyContent: 'center',
alignContent: 'center',
}
});
I include the object in the other page in this way:
<ButtonNavigation next={'NumberVehicle'} prev={'Home'}/>
In your <ButtonNavigation next={'NumberVehicle'} prev={'Home'}/> you need to add the navigation object as a prop. So something like:
<ButtonNavigation
next={'NumberVehicle'}
prev={'Home'}
navigation={this.props.navigation}
/>
(Make sure to replace this.props.navigation if it differs from the actual navigation object)
And after you can do:
const { next, prev, navigation } = this.props;
and
navigation.navigate(..)
You can change this
<ButtonNavigation next={this.props.navigation.navigate('NumberVehicle')} prev={this.props.navigation.navigate('Home')}/>
<View style={styles.bottomContainer}>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.next} title='Indietro' color='#00b0ff'/>
</View>
<View style={styles.buttonContainer}>
<Button onPress={() => this.props.prev} title='Avanti' color='gold'/>
</View>
</View>

Progress Dialog in ReactNative

I'm pretty new to ReactNative world. Im struggling to find an api or a library that shows the Progress Dialog as below in React Native. I believe ActivityIndicator can be used, but it does not show as overlay. Can anyone help me how can I show as an overlay using styles or if there is good library to make this.
Thanks
Here is the code to Open Prgressbar:
import React from 'react';
import { Modal, View, Text, ActivityIndicator, Button } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { isProgress: false }
}
openProgressbar = () => {
this.setState({ isProgress: true })
}
render() {
return (
this.state.isProgress ?
<CustomProgressBar />
:
<View style={{ flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center' }}>
<Button title="Please click here to Open ProgressBar" onPress={this.openProgressbar} />
</View>
);
}
}
const CustomProgressBar = ({ visible }) => (
<Modal onRequestClose={() => null} visible={visible}>
<View style={{ flex: 1, backgroundColor: '#dcdcdc', alignItems: 'center', justifyContent: 'center' }}>
<View style={{ borderRadius: 10, backgroundColor: 'white', padding: 25 }}>
<Text style={{ fontSize: 20, fontWeight: '200' }}>Loading</Text>
<ActivityIndicator size="large" />
</View>
</View>
</Modal>
);
Expo url for live demo
https://snack.expo.io/#jitendra.mca13/progress-bar-demo
I would approach this by using the React Native Modal component with two Views.
This solution is a React solution and not native, so you will need to style your Modal accordingly for each platform.
import React from 'react';
import {
Modal,
View,
StyleSheet,
Text,
ActivityIndicator
} from 'react-native';
const ProgressDialog = ({ visible }) => (
<Modal
visible={visible}
>
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>Please Wait</Text>
<View style={styles.loading}>
<View style={styles.loader}>
<ActivityIndicator size="large" />
</View>
<View style={styles.loadingContent}>
<Text>Loading</Text>
</View>
</View>
</View>
</View>
</Modal>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, .5)',
alignItems: 'center',
justifyContent: 'center',
},
content: {
padding: 35,
backgroundColor: 'white'
},
title: {
fontSize: 18,
fontWeight: 'bold',
},
loading: {
flexDirection: 'row',
alignItems: 'center',
},
loader: {
flex: 1,
},
loadingContent: {
flex: 3,
fontSize: 16,
paddingHorizontal: 10,
}
})
export default ProgressDialog;
Here's a demo on Expo, of course you will probably want to tweak the CSS.
Android & iOS solution
Create a directory called ProgressDialog
Create index.ios.js and index.android.js
Paste the above code into both index.ios.js and index.android.js
Make CSS changes for iOS
You can use the below npm package which is a very easy and attractive loader with many options.
https://github.com/maxs15/react-native-spinkit
import React from 'react';
import { StyleSheet, View } from "react-native";
import Spinner from "react-native-spinkit";
export default LoadingCmpBig = props => {
return (
<View style={styles.container}>
<StatusBar barStyle="default" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
//backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
}
});
You can use this component and use it where you want to show the Progress. You just have to maintain the state for visible the loading or not in your render function of the component.