What is the best way to deal with empty error message in react native? - react-native

I have to display a error message called "No record available".
This is my scenario : -
API Call {
if (data){
loading == false
}
}
In my component
Render(){
{
data.length > 0 && this.state.loading == false ?
<Flat List/>
: null
}
{
data.length==0 ?
<Text>No Record found</Text>
: null
}
}
My Problem was , my message displays if data not found but it doesn't refresh.
I have to achieve a scenario like this -
when i open or navigate through a page then its first show blank then loader start and after API call if data not found then they display a message.

This is a working example of what you describe. When the component loads the data is empty until your API call runs in componentDidMount. I have emulated the API call with a timeout of 2 seconds. You need to switch out the setTimeout function in apiCall with your own fetch method and set the state in the callback of that function
import React, { Component } from 'react';
import { View, Text } from 'react-native';
class Test extends Component {
state = {
loading: false,
data: [],
};
componentDidMount() {
this.apiCall();
}
apiCall = () => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({
loading: false,
data: ['1', '2', '3'],
});
}, 3000);
};
render() {
if (this.state.loading) return <Text>Loading...</Text>;
if (this.state.data.length === 0) return <Text>No records found</Text>;
return (
<View>
<Text>Records found</Text>
</View>
);
}
}
export default Test;

You can bind your action and reducers data
here this is the example you want
import React, { Component } from 'react';
import {
Text,
View,
Dimensions,
FlatList,
ScrollView,
} from 'react-native';
import { connect } from 'react-redux';
import {showLoading, getProducts} from '../actions/productAction';
import * as Progress from 'react-native-progress';
class Data extends Component {
this.state = {
product: [],
loading: false
};
componentWillMount() {
this.setState({loading: true});
API CALL();
}
render() {
return (
<View>
{this.state.isLoading ?
<View>
<Progress.CircleSnail thickness={5} size={55} color={['#000000', '#000000', '#FFFFFF',]} />
</View>
:
null
}
{this.state.product.length === 0 && <View>
<Text>{"NO PRODUCT"}</Text>
</View>}
<FlatList
data={this.state.product}
/>
</View>
);
}
}
export default Data;

Related

Google login without firebase is not working in react native

I'm trying to implement google login without firebase in react-native. here i have generated Client ID and Client secret from google cloud and also added required dependency but still the state is not loaded and not showing any content.
I received this error in console: A non-recoverable sign in failure occurred
please help me if anyone is having idea about it
Thanks in advance!!
import {Text, View, Image} from 'react-native';
import React, {Component} from 'react';
import {GoogleSignin, statusCodes} from '#react-native-community/google-signin';
import {GoogleSigninButton} from '#react-native-community/google-signin';
export class GoogleLogin extends Component {
constructor(props) {
super(props);
this.state = {
userGoogleInfo: {},
loaded: false,
};
}
componentDidMount() {
GoogleSignin.configure({
webClientId:
'my Web client id',
offlineAccess: true,
});
}
signIn = async () => {
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
this.setState({
userGoogleInfo: userInfo,
loaded: true,
});
console.log(this.state.userGoogleInfo);
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log('e 1');
} else if (error.code === statusCodes.IN_PROGRESS) {
console.log('e 2');
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
console.log('e 3');
} else {
console.log(error.message);
}
}
};
render() {
return (
<View>
<GoogleSigninButton
onPress={this.signIn}
size={GoogleSigninButton.Size.Wide}
color={GoogleSigninButton.Color.Dark}
style={{width: '100%', height: 100}}
/>
{this.state.loaded ? (
<View>
<Text> {this.state.userGoogleInfo.dictionary}</Text>
console.log('Hello')
</View>
) : (
<Text> Not sign in</Text>
)}
</View>
);
}
}
export default GoogleLogin;

Lodash debounce not working all of a sudden?

I'm using a component I wrote for one app, in a newer app. The code is like 99% identical between the first app, which is working, and the second app. Everything is fine except that debounce is not activating in the new app. What am I doing wrong?
// #flow
import type { Location } from "../redux/reducers/locationReducer";
import * as React from "react";
import { Text, TextInput, View, TouchableOpacity } from "react-native";
import { Input } from "react-native-elements";
import { GoogleMapsApiKey } from "../../.secrets";
import _, { debounce } from "lodash";
import { connect } from "react-redux";
import { setCurrentRegion } from "../redux/actions/locationActions";
export class AutoFillMapSearch extends React.Component<Props, State> {
textInput: ?TextInput;
state: State = {
address: "",
addressPredictions: [],
showPredictions: false
};
async handleAddressChange() {
console.log("handleAddressChange");
const url = `https://maps.googleapis.com/maps/api/place/autocomplete/json?key=${GoogleMapsApiKey}&input=${this.state.address}`;
try {
const result = await fetch(url);
const json = await result.json();
if (json.error_message) throw Error(json.error_message);
this.setState({
addressPredictions: json.predictions,
showPredictions: true
});
// debugger;
} catch (err) {
console.warn(err);
}
}
onChangeText = async (address: string) => {
await this.setState({ address });
console.log("onChangeText");
debounce(this.handleAddressChange.bind(this), 800); // console.log(debounce) confirms that the function is importing correctly.
};
render() {
const predictions = this.state.addressPredictions.map(prediction => (
<TouchableOpacity
style={styles.prediction}
key={prediction.id}
onPress={() => {
this.props.beforeOnPress();
this.onPredictionSelect(prediction);
}}
>
<Text style={text.prediction}>{prediction.description}</Text>
</TouchableOpacity>
));
return (
<View>
<TextInput
ref={ref => (this.textInput = ref)}
onChangeText={this.onChangeText}
value={this.state.address}
style={[styles.input, this.props.style]}
placeholder={"Search"}
autoCorrect={false}
clearButtonMode={"while-editing"}
onBlur={() => {
this.setState({ showPredictions: false });
}}
/>
{this.state.showPredictions && (
<View style={styles.predictionsContainer}>{predictions}</View>
)}
</View>
);
}
}
export default connect(
null,
{ setCurrentRegion }
)(AutoFillMapSearch);
I noticed that the difference in the code was that the older app called handleAddressChange as a second argument to setState. Flow was complaining about this in the new app so I thought async/awaiting setState would work the same way.
So changing it to this works fine (with no flow complaints for some reason. maybe because I've since installed flow-typed lodash. God I love flow-typed!):
onChangeText = async (address: string) => {
this.setState(
{ address },
_.debounce(this.handleAddressChange.bind(this), 800)
);
};

nothing fires after fetch data from DB react native

i have index.js contain's Tabs each tab render the same component (animate.js) but with different props, the code as the following:
index.js
categoryList.map((item,index) => {
if(item.head_category == category_id)
return (
<Tab heading={item.category_name} key={index}>
<Animate category_id={item.category_id}/>
</Tab>
)
});
in the animate.js i receive the category_id number and fetch the data using redux , and the data gets back
for first animate.js rendering nothing fires after the data returns but if i switch the tabs everything works grate
animate.js
import React from 'react';
import { InteractionManager, StyleSheet, Text, View, Button } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import {LineDotsLoader} from 'react-native-indicator';
import {goodsAction} from './redux/actions'
class animate extends React.Component {
constructor(props) {
super(props);
this.state = {
category_id:'',
loading:true,
};
console.log('constructor');
}
componentDidMount(){
const { category_id, category_name } = this.props;
this.props.goodsAction({category_id}); // My fetching Action
console.log(`componentDidMount `);
}
componentWillReceiveProps(nextProps){
console.log(`componentWillReceiveProps`)
}
static getDerivedStateFromProps(props, state){
console.log(` getDerivedStateFromProps `);
return null;
}
componentDidUpdate(prevProps, prevState) {
console.log(` componentDidUpdate `);
}
renderPage() {
return (
<View style={{ flex: 1 }}>
// anything
</View>
);
}
render(props) {
console.log(`render`);
if (this.props.loading) {
// if redux still fetching return loading:true else return false
return(<View style={styles.container}><LineDotsLoader /></View>)}
return (
<View style={styles.container}>
{this.renderPage()} // or anything
</View>
);
}
}
const mapStateToProps = state => {
return {
error: state.goods.error,
loading: state.goods.loading,
goods: state.goods.goods
}
}
export default connect(mapStateToProps, { goodsAction })(animate);
my console image
Edit
and this is my goodsRedusers.js
import {
GOODS_LOADING_ATTEMPT,
GOODS_REFRESH_ATTEMPT,
GOODS_LOADED,
GOODS_FAILED
} from '../actions/types';
const INITIAL_STATE = { goods:[], loading: true, error: '', }
export default (state = INITIAL_STATE, action) => {
switch(action.type) {
case GOODS_LOADING_ATTEMPT://dispatch before connecting to db
return {...INITIAL_STATE, loading: true }
case GOODS_FAILED:
return {...INITIAL_STATE, loading: false, error: action.error }
case GOODS_LOADED://dispatch after data gets back
return {...INITIAL_STATE, loading: false, goods: action.goods }
default:
return state;
}
}
static getDerivedStateFromProps(props, state){
console.log(` getDerivedStateFromProps `);
return null;
}
Returning null won't update the state when props change, if that's what you want.
Try removing this code.

How do I go back in webview? I am using the react-navigation package in react-native

I installed the react-navigation package in react-native
I have implemented tab navigation and one of them is implemented in webview format.
My problem is that if I press the back physical button on Android, I go from the app itself to the previous tab, not back from the webview.
I've already applied the back button for the webview on the internet, but I have not done that.
I tried to display the onNavigationStateChange log when debugging, but it was not updated when url was moved after it was loaded at first startup. Here is the code I implemented:
import React from "react";
import {BackHandler} from "react-native";
import {WebView} from "react-native-webview";
class SermonScreen extends React.Component {
constructor(props) {
super(props);
}
static navigationOptions = {
header: null
};
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
_onNavigationStateChange(navState) {
console.log(navState);
this.setState({
canGoBack: navState.canGoBack
});
}
handleBackButton = () => {
console.log(this.state);
if (this.state.canGoBack === true) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<WebView
source={{uri: 'https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos' }}
ref={(webView) => this.webView = webView}
onNavigationStateChange={this._onNavigationStateChange.bind(this)}
/>
);
}
}
export default SermonScreen;
Following the official webview documnentation you could try to do this: https://github.com/react-native-community/react-native-webview/blob/master/docs/Guide.md#intercepting-hash-url-changes
In general you were almost there, however the way the YT navigation works made it impossible to be caught via the onNavigationStateChange, that's why we inject a JS code that intercepts these hash changes and posts a message to the parent component, we then catch it inside the onMessage handler and set the state variable properly. Copying the injectedJavaScript and onMessage properties to your example should solve your problem.
I prepared a component for you that seems to do what is needed:
* Sample React Native App
* https://github.com/facebook/react-native
*
* #format
* #flow
*/
import React, { Fragment } from "react";
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
BackHandler,
StatusBar
} from "react-native";
import { WebView } from "react-native-webview";
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions
} from "react-native/Libraries/NewAppScreen";
class App extends React.Component {
constructor(props) {
super(props);
this.startingUrl =
"https://m.youtube.com/channel/UCw3kP3qCCF7ZpLUNzm_Q9Xw/videos";
this.handleBackButton = this.handleBackButton.bind(this);
}
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
}
handleBackButton = () => {
console.log(this.state);
const { canGoBack } = this.state;
if (canGoBack) {
this.webView.goBack();
return true;
} else {
return false;
}
};
render() {
return (
<Fragment>
<WebView
source={{ uri: this.startingUrl }}
style={{ marginTop: 20 }}
ref={webView => (this.webView = webView)}
injectedJavaScript={`
(function() {
function wrap(fn) {
return function wrapper() {
var res = fn.apply(this, arguments);
window.ReactNativeWebView.postMessage('navigationStateChange');
return res;
}
}
history.pushState = wrap(history.pushState);
history.replaceState = wrap(history.replaceState);
window.addEventListener('popstate', function() {
window.ReactNativeWebView.postMessage('navigationStateChange');
});
})();
true;
`}
onMessage={({ nativeEvent: state }) => {
if (state.data === "navigationStateChange") {
// Navigation state updated, can check state.canGoBack, etc.
this.setState({
canGoBack: state.canGoBack
});
}
}}
/>
</Fragment>
);
}
}
export default App;
The response above was perfect. I set the state true for canGoBack though; I was getting a null error, so:
constructor(props) {
super(props);
this.startingUrl = "https://app.vethorcardpag.com.br/GIF/login/0/";
this.state = {
canGoBack : true
}
this.handleBackButton = this.handleBackButton.bind(this);
}
Here is a simple solution using the magic of React's State.
Hope this helps.
import React, { useRef, useState } from 'react'
export default function Component () {
// This is used to save the reference of your webview, so you can control it
const webViewRef = useRef(null);
// This state saves whether your WebView can go back
const [webViewcanGoBack, setWebViewcanGoBack] = useState(false);
const goBack = () => {
// Getting the webview reference
const webView = webViewRef.current
if (webViewcanGoBack)
// Do stuff here if your webview can go back
else
// Do stuff here if your webview can't go back
}
return (
<WebView
source={{ uri: `Your URL` }}
ref={webViewRef}
javaScriptEnabled={true}
onLoadProgress={({ nativeEvent }) => {
// This function is called everytime your web view loads a page
// and here we change the state of can go back
setWebViewcanGoBack(nativeEvent.canGoBack)
}}
/>
)
}
Original answer
https://stackoverflow.com/a/74500469/7823800

react-native redux props changes back to undefined

I'm trying to add a filter to my app, but for some reason selectedValue in the <Picker> component doesn't stick with the option I select. I can see the filter text changing from "all" to "lobby" in the top left, however as soon as the player list fully renders, it changes back to "all." and playerListFilterType prop is set to undefined. I stepped through the code in a debugger, and it stays "lobby" until the list re-renders. The action itself works, so the list is showing accurate results.
Here's what my code looks like:
import React from 'react'
import { View, Picker } from 'react-native'
import PlayerList from '../components/PlayerList'
import { fetchPlayerListAsync, filterPlayers } from '../redux/actions/player_actions';
import NavigationHeaderTitle from '../components/NavigationHeaderTitle'
import PlayerStatusFilterPicker from '../components/pickers/PlayerStatusFilterPicker'
import { connect } from 'react-redux'
class PlayerListScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
const playerStatusFilterPicker = (
<PlayerStatusFilterPicker
playerListFilterType={navigation.getParam('playerListFilterType')}
filterPlayers={navigation.getParam('filterPlayers')}
playerList={navigation.getParam('playerList')}
/>
)
return {
headerTitle: navigation.getParam('headerButton'),
headerRight: playerStatusFilterPicker
}
}
async componentDidMount() {
await this.fetchPlayersAsync();
}
setNavigationParams = () => {
this.props.navigation.setParams({
headerButton: this.headerButton,
playerList: this.props.playerList,
playerListFilterType: this.props.playerListFilterType,
filterPlayers: this.props.filterPlayers
})
}
// navigation header element
headerButton = () => (
<NavigationHeaderTitle
handleDataRequest={this.fetchPlayersAsync}
titleMessage={(this.props.fetchingData) ? 'fetching list of players' : `${this.props.playerList.length} online`}
/>
)
fetchPlayersAsync = async () => {
await this.props.fetchPlayerListAsync();
this.setNavigationParams()
}
render() {
return (
<View>
<PlayerList
playerList={this.props.playerList}
fetchingData={this.props.fetchingData}
handleDataRequest={this.fetchPlayersAsync}
/>
</View>
)
}
}
const mapStateToProps = state => {
return {
fetchingData: state.player.fetchingData,
playerList: state.player.playerList,
unfilteredPlayerList: state.player.unfilteredPlayerList,
playerListFilterType: state.player.playerListFilterType
}
};
export default connect(mapStateToProps, { fetchPlayerListAsync, filterPlayers })(PlayerListScreen)
and here's what the filter component looks like, but I don't think the problem lies here:
import React, { Component } from "react";
import {
View,
Picker
} from "react-native";
import * as constants from '../../constants'
class PlayerStatusFilterPicker extends Component {
render() {
return (
<View>
<Picker
selectedValue={this.props.playerListFilterType}
onValueChange={(itemValue) => this.props.filterPlayers(itemValue, this.props.playerList)}
style={{ height: 40, width: 100 }}
>
<Picker.Item label='all' value='all' />
<Picker.Item label="lobby" value={constants.IN_LOBBY} />
<Picker.Item label="in game" value={constants.IN_GAME} />
</Picker>
</View>
);
}
}
export default PlayerStatusFilterPicker;
Here's what the reducer looks like:
// show only the players that are waiting in the main lobby
case actionTypes.SHOW_PLAYERS_IN_LOBBY: {
const filteredList = action.payload.filter(player => player.status === constants.IN_LOBBY)
return { playerList: filteredList, playerListFilterType: constants.IN_LOBBY, fetchingData: false }
}
// show only the players that are currently playing
case actionTypes.SHOW_PLAYERS_IN_GAME: {
const filteredList = action.payload.filter(player => player.status === constants.IN_GAME)
return { playerList: filteredList, playerListFilterType: constants.IN_LOBBY, fetchingData: false }
}
Fixed it by using componentDidUpdate lifecycle method. Like so:
componentDidUpdate(prevProps) {
if (this.props.playerListFilterType != prevProps.playerListFilterType) {
this.props.navigation.setParams({
playerListFilterType: this.props.playerListFilterType
})
}
}