React Native: Dispatching action to fetch data before rendering (Redux) - reactjs

I'm developing a react native app with redux as a state management and trying the following implementation:
Fetching data from Server (Firebase) before rendering the app. I have a loading screen, which is working fine, but i cant dispatch my function in it for fetching the data from server and write directly to the current state. I dont see that the state is changed, but when I saving the code (as it is) again the state is updated as I expected. So i think my code is fetching the data after the first rendering is done.
I cant figure out the the problem. Cause i put my code for dispatching in the "useeffect" hook as i should or not?
import React, {useEffect} from 'react';
import {ActivityIndicator, View} from 'react-native';
import {Images, useTheme} from '#config';
import {Image, Text} from '#components';
import styles from './styles';
import {ApplicationActions} from '#actions';
import { store } from '/Users/user1/KitaList/app/store/index.js'
export default function Loading({navigation}) {
const {colors} = useTheme();
const onProcess = () => {
setTimeout(() => {
navigation.replace('Main');
}, 2500);
};
useEffect(() => {
store.dispatch(ApplicationActions.ongetKitadata()); //Executing dispatchting getting data
onProcess();
}, []);
return (
<View style={styles.container}>
<View style={{alignItems: 'center'}}>
<Image source={Images.logo} style={styles.logo} resizeMode="contain" />
<Text title1 style={{marginTop: 10}}>
MyApp
</Text>
<Text headline primaryColor style={{marginTop: 10}}>
MyApp DIRECTORY
</Text>
</View>
<ActivityIndicator
size="large"
color={colors.text}
style={{
position: 'absolute',
top: 260,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
}}
/>
</View>
);
}

Related

React Native - React Navigation Cannot use Hooks in DrawerContent

I am developing an e-commerce application using React Native and I am trying to use useState in the drawerContent and it tells me this
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Thank you in advance for your answers.
Here's the code
import React, { useState } from 'react'
import { View, Text, TouchableOpacity, FlatList, StyleSheet, StatusBar } from 'react-native'
import IonIcons from "react-native-vector-icons/Ionicons"
import { categories } from '../../../services/DataTest'
import DrawerSearch from './DrawerSearch'
import DrawerItem from './DrawerItem'
export default function DrawerContent (props) {
const [search, setSearch] = useState("");
return (
<View>
<TouchableOpacity
style={styles.customDrawerTouch}
>
<View style={styles.backButtonRow}>
<IonIcons
name="ios-arrow-back"
size={25}
style={styles.customDrawerIcon}
color="#666666"
/>
<Text style={{ color: '#666666' }}>Back to Components</Text>
</View>
</TouchableOpacity>
<DrawerSearch value={search} setValue={setSearch}/>
<FlatList
data={categories}
keyExtractor={(item, index) => index.toString()}
renderItem={DrawerItem}
/>
</View>
);
}
const styles = StyleSheet.create({
customDrawerTouch: {
marginTop: StatusBar.currentHeight,
paddingLeft: 13,
paddingTop: 15,
},
customDrawerIcon: {
paddingRight: 10
},
backButtonRow: {
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 17,
paddingLeft: 3,
borderBottomColor: '#F0F0F0',
borderBottomWidth: 1,
},
});
I'm using this component here
import * as React from 'react';
import { View, StyleSheet, StatusBar } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import HeaderCategorie from '../../components/categories/index/HeaderCategorie';
import SearchBar from '../../components/home/index/SearchBar';
import DrawerContent from '../../components/categories/index/DrawerContent';
const Drawer = createDrawerNavigator();
function CategoriesScreen({ navigation }) {
return (
<View style={styles.container}>
<HeaderCategorie navigation={navigation}/>
<View style={styles.headerSearch}>
<SearchBar />
</View>
</View>
)
}
export default function Categories() {
return (
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
<Drawer.Screen name="Categories" component={CategoriesScreen} />
</Drawer.Navigator>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
marginTop: StatusBar.currentHeight,
},
headerSearch: {
marginVertical:10
},
headerSearchText: {
fontWeight:"bold",
fontSize:35,
marginLeft:20,
marginVertical:15,
}
});
Reason: By using drawerContent={DrawerContent}, you are actually passing the reference of the DrawerContent function, which ends up breaking rules of hooks.
So to resolve this, change the following line:
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
to this
<Drawer.Navigator initialRouteName="Categories"
drawerContent={(props)=> <DrawerContent {...props}/>} // here
screenOptions={{headerShown:false}}
>
demo snack

React-Native TypeError with undefined

I am following this video tutorial on Redux. I followed the code exactly as the person from the tutorial but when the app renders I get an error saying -
TypeError: undefined is not an object (evaluating 'this.state'
Here's the error stack:
And here's the code that I use for the component:
import React from 'react'
import {View, Text, StyleSheet, TextInput, TouchableOpacity} from 'react-native'
import {Ionicons} from '#expo/vector-icons'
import {connect} from 'react-redux' // used to connect the Store with this component
function AddTodo() {
state = {
text: ''
}
addTodo = (text) => {
this.props.dispatch({type:'ADD_TODO', text})
this.setState({text: ''})
}
return (
<View style={{flexDirection: 'row', marginHorizontal: 20}}>
<TextInput
onChangeText={ (text) => this.setState({text})}
value={this.state.text}
placeholder="Eg. Create new Video"
style={{borderWidth: 1, borderColor:'#eaeaea',
backgroudColor:'#f2f2e1', height: 50, flex:1, padding: 5}} />
<TouchableOpacity onPress={() => this.addTodo(this.state.text)}>
<View style={{height:50, backgroundColor:'#f2f2e1', alignItems:'center', justifyContent:'center'}}>
<Ionicons name="md-add" size={30} style={{color:'#de9595', padding:10}}/>
</View>
</TouchableOpacity>
</View>
)
}
export default connect()(AddTodo) //This is how we connect the Store with the component
const styles = StyleSheet.create({
container:{
flex:1,
alignItems:'center',
justifyContent:'center'
}
})
Why am I getting this error?
You can only use this in a class, referring to the instance of that class. In your functional components you cannot have a persistent state without using the hook useState.
Do this instead:
const [state, setState] = useState({text: ''});
And access use your state like so: state.text.
Always call your hooks at the top level, before conditionals and renders.

Show loading indicator between navigation of pages in a Webview

I have a url in the Webview like https://www.nytimes.com/ The current code works inital page load but If I type tap anything in the link, the page takes a while to load and there are no loading indicators in the website. Is there any way we can put a page loading indicator in React Native while we click on any link or page loading specially if it is server side rendered like next js?
Here is my ReactNative code.
import * as React from 'react';
import {
View,
Text,
Image,
SafeAreaView,
ScrollView,
TextInput,
TouchableOpacity,
} from 'react-native';
import styles from './styles';
import { WebView } from 'react-native-webview';
// import WelcomeSwiper from '../../components/WelcomeScreen/WelcomeSwiper';
import LoadingIcon from '../../components/Loading/index.js';
const WebView = ({ navigation }) => {
return (
<SafeAreaView style={styles.container}>
<WebView
javaScriptEnabled={true}
domStorageEnabled={true}
renderLoading={LoadingIcon}
source={{ uri: 'https://www.nytimes.com/ ' }}
startInLoadingState={true}
/>
</SafeAreaView>
);
};
export default WebView;
Here is my loading component
import React from 'react';
import { StyleSheet, Platform, ActivityIndicator } from 'react-native';
const LoadingIcon = () => {
return (
<ActivityIndicator
color='#009688'
size='large'
style={styles.ActivityIndicatorStyle}
/>
);
}
export default LoadingIcon;
const styles = StyleSheet.create(
{
WebViewStyle:
{
justifyContent: 'center',
alignItems: 'center',
flex: 1,
marginTop: (Platform.OS) === 'ios' ? 20 : 0
},
ActivityIndicatorStyle: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
alignItems: 'center',
justifyContent: 'center'
}
});
we can use these two approaches to get the result:
You can check if the WebView is loading something or not with the onLoadProgress method. This method gives you a number between 0 and 1. If the page is fully loaded it will return number 1, update your state and show the ActivityIndicator according to it:
you can use onLoadStart and onLoadEnd to update your state and show the ActivityIndicator according to it!
for more info check the: https://github.com/react-native-community/react-native-webview/blob/master/docs/Reference.md#onloadprogress
you can also use your ActivityIndicator wrapped by WebView, *do not Forget this method works in ios for android put it outside of WebView
and this is a working code sample for you:
import React, {useState} from 'react';
import {View, Text, SafeAreaView, ActivityIndicator} from 'react-native';
import {WebView} from 'react-native-webview';
function WebViewTest() {
const [isLoading, setLoading] = useState(false);
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{uri: 'https://www.nytimes.com/'}}
onLoadStart={(syntheticEvent) => {
setLoading(true);
}}
onLoadEnd={(syntheticEvent) => {
setLoading(false);
}} />
{isLoading && (
<View style={{flex: 10, backgroundColor: 'white'}}>
<ActivityIndicator
color="#009688"
size="large"
// style={{position: 'absolute', left: 200, top: 300}}
/>
</View>
)}
</SafeAreaView>
);
}
export default WebViewTest;
I hope it helps
I use onLoadProgress to solve this issue.
renderLoading function is just called at the initial loading state of webview component. Using renderLoading is not helpful to show activity indicators at any tap on page links or navigating between webview pages.
Checking onLoadStart and onLoadEnd is useful in android but in iOS onLoadEnd is not called in the back navigation gesture which results in endless spinning of activity indicator.
onLoadProgress returns a number between 0-1 while webview is in the loading state. You check its progress state and update your activity indicator state.
For more information about onLoadProgress: onLoadProgress
Here is a working code example for you:
import React, {useState} from 'react';
import {View, Text, SafeAreaView, ActivityIndicator} from 'react-native';
import {WebView} from 'react-native-webview';
function WebViewTest() {
const [isLoading, setLoading] = useState(false);
return (
<SafeAreaView style={{flex: 1}}>
<WebView
source={{uri: 'https://www.nytimes.com/'}}
onLoadProgress={({nativeEvent}) => {
if (nativeEvent.progress != 1 && isLoading == false ) {
setLoading(true)
} else if (nativeEvent.progress == 1 ) {
setLoading(false)
}
}}
/>
{isLoading && (
<View style={{flex: 10, backgroundColor: 'white'}}>
<ActivityIndicator
color="#009688"
size="large"
// style={{position: 'absolute', left: 200, top: 300}}
/>
</View>
)}
</SafeAreaView>
);
}
export default WebViewTest;

Is there a way to have a button render my camera component in React Native?

I am new to react native and am trying to make my camera component pop up whenever I click on a button. I am able to get the camera to render in App.js, but the minute I try to get it rendering in the component it just doesn't work. Should I use state to get this to render? If so, why doesn't react native allow you to just render a component within a component? I'm trying to understand the concept of components calling other components. Heres my code:
import React, {Component} from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';
import DeviceCamera from './camera';
class CameraButton extends Component {
render() {
return (
<View style={styles.container}>
<Text> Button to Open Camera </Text>
<Button
onPress={() => {
<DeviceCamera />;
}}
title="click me to open the camera!"
color="#841584"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
export default CameraButton;
I was trying to use the on press function to call the camera component but perhaps I am misunderstanding something.
Yeah, I would probably just use state here
class CameraButton extends Component {
showCamera = () => this.setState({ showCamera: true });
render() {
return (
<View style={styles.container}>
<Text> Button to Open Camera </Text>
<Button
onPress={this.showCamera}
title="click me to open the camera!"
color="#841584"
/>
{this.state.showCamera && <DeviceCamera />}
</View>
);
}
}
With JSX, you can use foo && <Bar />; if foo evaluates to something truthy, then it will render your component, otherwise it will not.

How to use the length attribute in react native?

I am currently learning to build a shopping app and working on the cart functionality of the react native application. I have made a separate shopping cart icon component which I am passing into the main application. However, when I try to display the number of items in the shopping cart icon using the length attribute, the length is not getting displayed. I am a beginner and any help will be appreciated.
ShoppingCartIcon.js
import React, { Component } from 'react';
import { View, Text, Picker,Button } from 'react-native';
import { connect } from 'react-redux';
import Icon from 'react-native-vector-icons/FontAwesome';
import { Actions } from 'react-native-router-flux';
goToNextScreen = () => {
Actions.cartScreen();
}
const ShoppingCartIcon = (props) => (
<View style={{position:'absolute', top:0,left:0,width: 40,height: 40,borderRadius: 40/2,paddingTop:3}}>
<View style={{
position: 'absolute', height: 20, width: 20, borderRadius: 10, backgroundColor: 'rgba(6,21,42,0.8)', top:0, left: 5, alignItems: 'center', justifyContent: 'center', zIndex: 2000
}}>
<Text style={{ color: 'white', fontWeight: 'bold',fontSize:10 }}>{props.cartItems.length}</Text>
</View>
<Icon onPress={() => this.goToNextScreen()} name="shopping-cart" style ={{fontSize:30, position:'absolute', top:0,padding:7, left:0, color:'#fe003a'}} />
</View>
)
const mapStateToProps = (state) => {
return {
cartItems: state
}
}
export default connect(mapStateToProps)(ShoppingCartIcon);
Output:
The image of the shopping cart icon
In the mapStateToProps function, you set cartItems: state, but maybe you'd have to do cartItems: state.YourReducerName.yourArrayName. Could you post the reducer code as well?

Resources