How to convert methods from Library to fit functional component - reactjs

Need help converting RNLocation.requestPermission and _startUpdatingLocation to work with function components in react native.
Not sure how to approach this
LatLon Component File
import React, {useState} from "react";
import { SafeAreaView, StyleSheet, Text, } from "react-native";
import RNLocation from "react-native-location";
import moment from "moment";
import styled from 'styled-components/native';
function LatLonBox(){
const [currentLocation, setLocation] = useState(null);
const styles = StyleSheet.create({
Custom_Text:{
textAlign:'center',
fontSize:15
}
});
const LocationContainer = styled.View`
marginTop: 10;
marginBottom: 10;
`;
//How to make this work in functional style component
RNLocation.requestPermission({
ios: "whenInUse"
}).then(granted => {
if (granted) {
this._startUpdatingLocation();
}
});
}
//How to make this work in functional style component
_startUpdatingLocation = () => {
this.locationSubscription = RNLocation.subscribeToLocationUpdates(
locations => {
this.setState({ location: locations[0] });
}
);
};
return(
<SafeAreaView>
<React.Fragment>
<LocationContainer>
<Text style = {styles.CustomText}>{currentLocation.longitude}</Text>
<Text style = {styles.CustomText}>{currentLocation.latitude}</Text>
</LocationContainer>
</React.Fragment>
</SafeAreaView>
);
}
export default LatLonBox;
Screen File //Just extra if needed
import React from 'react';
import { SafeAreaView } from 'react-native';
import { StackParamList } from '../types';
import { BottomSheetModalProvider } from '#gorhom/bottom-sheet';
import { NativeStackScreenProps } from '#react-navigation/native-stack';
import LatLonBox from '../components/LatLonBox';
export default ({}: NativeStackScreenProps<StackParamList, 'Main'>): JSX.Element => {
return (
<BottomSheetModalProvider>
<SafeAreaView style={{ flex: 1}}>
<LatLonBox/> //where LatLon will be placed
</SafeAreaView>
</BottomSheetModalProvider>
);
};
Link to RNLocation Library

You can check this
Hey hope this helps, feel free for doubts:
const _startUpdatingLocation = () => {
const locationSubscription = RNLocation.subscribeToLocationUpdates(
locations => {
setLocation(locations[0])
}
);
};
const requestPermission = useCallback(() => {
RNLocation.requestPermission({
ios: "whenInUse"
}).then(granted => {
if (granted) {
_startUpdatingLocation();
}
});
}
},[_startUpdatingLocation])
useEffect(() => {
requestPermission()
},[requestPermission])
//How to make this work in functional style component

Related

Expo Refresh only working in certain components

so I am just digging into building apps with React Native/Expo and I have come across a weird behavior that I am not sure why is happening. I have a dashboard component that returns a 'UsersFlatList' component
const DashBoard = () => {
return (
<>
<View style={styles.dashboard__wrapper}>
<TextInput
placeholder="why is this working??
"
style={styles.form__input}
></TextInput>
<UsersFlatList />
</View>
</>
)
}
If I change that text - I see the changes happening in expo go immediately. However if I make changes to the data in the flatlist, I don't see any changes at all and I have to refresh the app to see any of the updates changes.
import { useEffect, useState } from "react"
import { Text, View, SafeAreaView, FlatList, StyleSheet } from "react-native"
import UserCard from "./UserCard"
import { collection, getDocs } from "firebase/firestore"
import db from "../firebaseConfig"
const UsersFlatList = () => {
const [onlineUsers, setOnlineUsers] = useState([])
useEffect(() => {
const fetchAllData = async () => {
const foundUsers = await getDocs(collection(db, "users"))
const currentUsers = []
foundUsers.forEach((user) => currentUsers.push(user.data()))
setOnlineUsers(() => currentUsers)
}
const matchedUsers = fetchAllData()
}, [])
return (
<FlatList
data={onlineUsers}
renderItem={UserCard}
keyExtractor={(item) => item.email}
></FlatList>
)
}
export default UsersFlatList
import { Text, View, StyleSheet } from "react-native"
const UserCard = ({ item }) => {
console.log(item)
return (
<View style={styles.userCard}>
<View style={styles.online__icon}></View>
<Text>{item.username} is a</Text>
</View>
)
}
const styles = StyleSheet.create({})
export default UserCard
Appreciate this is a long one to read but any help or a pointer would be amazing thanks

Global screen loader in react

I am looking for a solution for using a global screen loader in react.
I am not that much familiar to react context, but I was wondering if that could help me here.
Basically I am introducing a screenloader and I was thinking that maybe the best way would be to have a global loader somewhere in main component.So to conclude:
I want to have global loader in main component
I want to update the state of global loader wherever I want in app
I don't want to pollute all the components with ScreenLoaders where I need to use it
I want to use hooks for it
So is there a way to have a global state of loader/loaderText and setting and resetting whenever needed using context?
If there is a simple way to do it, then do you think there might be any drawbacks of using such solution? Maybe that's an overkill for it.
What about creating a custom hook, useLoading, which can be used in any component that gives access to loading and setLoading from global context?
// LoadingContext.js
import { createContext, useContext, useState } from "react";
const LoadingContext = createContext({
loading: false,
setLoading: null,
});
export function LoadingProvider({ children }) {
const [loading, setLoading] = useState(false);
const value = { loading, setLoading };
return (
<LoadingContext.Provider value={value}>{children}</LoadingContext.Provider>
);
}
export function useLoading() {
const context = useContext(LoadingContext);
if (!context) {
throw new Error("useLoading must be used within LoadingProvider");
}
return context;
}
// App.jsx
import { LoadingProvider } from "./LoadingContext";
function App() {
return (
<LoadingProvider>
<RestOfYourApp />
</LoadingProvider>
);
}
// RestOfYourApp.js
import { useLoading } from "./LoadingContext";
function RestOfYourApp() {
const { loading, setLoading } = useLoading();
return (
{loading && <LoadingComponent />}
...
);
}
useLoader.js (hook)
import React, { useState } from "react";
import Loader from "./loader";
const useLoader = () => {
const [loading, setLoading] = useState(false);
return [
loading ? <Loader /> : null,
() => setLoading(true),
() => setLoading(false),
];
};
export default useLoader;
loader.js (loader componenet)
import React from "react";
import styled from "styled-components";
import spinner from "./loader.gif"; // create gif from https://loading.io
import Color from "../../Constant/Color";
const Loader = () => {
return (
<LoaderContainer>
<LoaderImg src={spinner} />
</LoaderContainer>
);
};
const LoaderContainer = styled.div`
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
background: ${Color.greyBg};
z-index: 100;
`;
const LoaderImg = styled.img`
position: absolute;
`;
export default Loader;
Using Loader hook
import useLoader from "../../../hooks/loader/useLoader"; /// import loader hook
const App = (props) => {
const [loader, showLoader, hideLoader] = useLoader(); //initialize useLoader hook
useEffect(() => {
showLoader(); /// loading starts
Axios.post("url")
.then((res) => {
hideLoader(); // loading stops
})
.catch((error) => {
hideLoader();// loading stops
});
}, []);
return (
<>
{loader} /// important
//// add your elements /////
</>
)
}
export default App;
Some more easy way
Create Provider with context and hook within single file
import React, {useRef} from 'react';
import {Loader} from '#components';
const LoaderContext = React.createContext();
export function LoaderProvider({children}) {
const ref = useRef();
const startLoader = () => ref.current.start();
const stopLoader = () => ref.current.stop();
const value = React.useMemo(
() => ({ref, startLoader, stopLoader}),
[ref, startLoader, stopLoader]
);
return (
<LoaderContext.Provider value={value}>
{children}
<Loader ref={ref} />
</LoaderContext.Provider>
);
}
export const useLoader = () => React.useContext(LoaderContext);
in App.js add provider
import {StoreProvider} from 'easy-peasy';
import React from 'react';
import {StatusBar, View} from 'react-native';
import colors from './src/assets/colors';
import Navigation from './src/navigation/routes';
import {LoaderProvider} from './src/providers/LoaderProvider';
import {ToastProvider} from './src/providers/ToastProvider';
import store from './src/redux/store';
import globalStyles from './src/styles/index';
import('./src/helpers/ReactotronConfig');
function App() {
return (
<StoreProvider store={store}>
<StatusBar
barStyle="light-content"
backgroundColor={colors.backgroundDark}
translucent={false}
/>
<ToastProvider>
<LoaderProvider>
<View style={globalStyles.flex}>
<Navigation />
</View>
</LoaderProvider>
</ToastProvider>
</StoreProvider>
);
}
export default App;
And in any screen use like this way
import {useLoader} from '../../providers/LoaderProvider';
const {startLoader, stopLoader} = useLoader();
Loader.js
import React, {forwardRef, useImperativeHandle, useState} from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import {wp} from '../../styles/responsive';
function Loader(props, ref) {
const [loading, setLoading] = useState(0);
useImperativeHandle(
ref,
() => ({
start: () => {
const loadingCount = loading + 1;
setLoading(loadingCount);
},
stop: () => {
const loadingCount = loading > 0 ? loading - 1 : 0;
setLoading(loadingCount);
},
isLoading: () => loading >= 1,
}),
[],
);
if (!loading) {
return null;
}
return (
<View style={styles.container}>
<ActivityIndicator size={'small'} color={'#f0f'} />
</View>
);
}
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFill,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#11111150',
zIndex: 999,
elevation: 999,
},
});
export default forwardRef(Loader);
You can use this package for simple react loading : https://www.npmjs.com/package/react-global-loading
Usage :
import { GlobalLoading, showLoading } from 'react-hot-toast';
const App = () => {
const show = () => {
showLoading(true);
setTimeout(() => {
showLoading(false);
}, 1000);
};
return (
<div>
<button onClick={show}>Show Loading</button>
<GlobalLoading />
</div>
);
};

Accessing Refs in React Functional Component

Im using this package to add credit cards to my app, how would you åccess the refs as in the example when using this in a functional component?
This is how they show to update values:
this.refs.CCInput.setValues({ number: "4242" });
I don't know how to access that inside a functional component?
This is my component to Edit a card, and I want to add the current values to the inputs.
import React, {useContext, useState, useRef} from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import {CreditCardInput} from 'react-native-input-credit-card';
import Store from '../../store/context';
const styles = StyleSheet.create({
});
export default function EditCard(props) {
const {navigate} = props.navigation;
const {cardNumber} = props.navigation.state.params;
const {state, dispatch} = useContext(Store);
const [card, setCard] = useState(
state.cards.find(card => {
return card.values.number === cardNumber;
}),
);
const _onChange = form => {
if (form.valid) {
setCard(form);
}
};
const updateCard = () => {
dispatch({
type: 'ADD_CARD',
payload: card,
});
navigate.goBack();
};
return (
<View style={styles.container}>
<CreditCardInput
validColor={'#47B278'}
invalidColor={'#E23C3C'}
placeholderColor={'#efefef'}
onChange={_onChange}
requiresName
/>
<TouchableOpacity
style={
card
? card.valid
? styles.button
: styles.buttonDisabled
: styles.buttonDisabled
}
disabled={card ? (card.valid ? false : true) : true}
onPress={updateCard}>
<Text style={styles.buttonText}>Update Credit Card</Text>
</TouchableOpacity>
</View>
);
}
You need to use useRef hook:
const cciRef = useRef();
<CreditCardInput ref={cciRef}/>;
// cciRef.current holds the reference
export default function EditCard(props) {
const cciRef = useRef();
useEffect(() => {
console.log(cciRef.current);
cciRef.current.setValues({ number: "4242" });
}, []);
return (
<View style={styles.container}>
<CreditCardInput ref={cciRef} />
</View>
);
}

How to check propTypes in child component?

So i have a two components, and trying to validate prop-types gives an error:
TypeError: TypeError: Cannot read property 'style' of undefined
This is my HomeScreen component:
import React, { Component } from 'react';
import {
Image,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
import { RobotoText } from '../components/StyledText';
export default class HomeScreen extends Component {
render() {
const { container } = styles;
return (
<View style={container}>
<RobotoText style={{ color: 'green' }}> Test </RobotoText>
</View>
);
}
}
And the StyledText file where the error happens:
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
const MonoText = props => {
const { style } = props;
return <Text {...props} style={[style, { fontFamily: 'space-mono' }]} />;
};
const RobotoText = props => {
const { style } = props;
return <Text {...props} style={[style, { fontFamily: 'Roboto' }]} />;
};
MonoText.defaultProps = {
style: { color: 'black' }
};
MonoText.propTypes = {
style: Text.PropTypes.style
};
RobotoText.defaultProps = {
style: { color: 'black' }
};
RobotoText.propTypes = {
style: Text.PropTypes.style
};
export { MonoText, RobotoText };
What would be the correct way the define this propTypes?
You need to define the propTypes using the PropTypes library you imported, not the Text object.
Also style is not a valid proptype, what you are passing is actually an object
So
MonoText.propTypes = {
style: PropTypes.object
};
RobotoText.propTypes = {
style: PropTypes.object
};
Here's a link to the docs in case you want to know more
https://reactjs.org/docs/typechecking-with-proptypes.html

TypeError: undefined is not an object(evaluating '_props.listMessagesQuery.listMessages') in ReactNative

Iam new to react-native and aws appsync.We are trying to display a list of messages.But when i run react-native run-android it is throwing an error saying
TypeError: undefined is not an object(evaluating '_props.listMessagesQuery.listMessages')
[Below is the screenshot url of the error]
https://i.stack.imgur.com/b1Wlj.png
Chat.js
import React,{Component} from 'react';
import ChatInput from './ChatInput';
import ChatMessages from './ChatMessages';
import { graphql, compose } from 'react-apollo';
import listMessages from './querys/listMessages';
import createMessage from './querys/createMessage';
import gql from 'graphql-tag';
import {
Platform,
StyleSheet,
Text,
View,
scrollIntoView
} from 'react-native';
class Chat extends Component {
state = {
message: '',
}
render() {
return (
<View className='Chat'>
<ChatMessages
messages={this.props.listMessagesQuery.listMessages || []}
endRef={this._endRef}
/>
<ChatInput
message={this.state.message}
onTextInput={(message) => this.setState({message})}
onResetText={() => this.setState({message: ''})}
onSend={this._onSend}
/>
</View>
);
}
_onSend = () => {
//console.log(`Send: ${this.state.message}`)
this.props.createMessageMutation({
variables: {
text: this.state.message,
sentById: this.props.userId
}
})
}
/*
* AUTO SCROLLING
*/
_endRef = (element) => {
this.endRef = element
}
componentDidUpdate(prevProps) {
// scroll down with every new message
if (prevProps.listMessagesQuery.listMessages !== this.props.listMessagesQuery.listMessages && this.endRef) {
this.endRef.scrollIntoView()
}
}
}
export default compose(
graphql(listMessages, {
options: {
fetchPolicy: 'cache-and-network'
},
props: (props) => ({
posts: props.listMessagesQuery.listMessages && props.listMessagesQuery.listMessages.Message,
})
}))(Chat)
App.js
import React,{ Component} from 'react';
import * as AWS from 'aws-sdk';
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
import gql from 'graphql-tag';
import { graphql,compose} from 'react-apollo';
import generateStupidName from 'sillyname';
import localStorage from 'react-native-sync-localstorage';
import Chat from './Chat';
import { Async } from 'react-async-await';
import createPerson from './querys/createPerson';
const CHAT_USER_NAME_KEY = 'CHAT_USER_NAME'
const CHAT_USER_ID_KEY = 'CHAT_USER_ID'
class App extends Component {
async componentDidMount() {
let name = localStorage.getItem(CHAT_USER_NAME_KEY)
if (!name) {
name = generateStupidName()
const result = await this.props.createPersonMutation({
variables: { name }
})
localStorage.setItem(CHAT_USER_NAME_KEY, result.data.createPerson.name);
localStorage.setItem(CHAT_USER_ID_KEY, result.data.createPerson.id);
}
}
render() {
const name = localStorage.getItem(CHAT_USER_NAME_KEY)
const userId = localStorage.getItem(CHAT_USER_ID_KEY)
return (
<View style={styles.container}>
<Chat name={name} userId={userId} />
</View>
);
}
}
// const createPerson =gql`
// mutation createPerson($name:String!){
// createPerson(input :{
// name : $name
// }){
// id
// name
// }
// }
// `
// export default graphql(createPerson,{name:'createPersonMutation'})(App)
export default compose(
graphql(createPerson, {name:'createPersonMutation'}))(App)
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
Iam not understanding this error Please help me.Thanks! in Advance
Please check the format of
this.props.listMessagesQuery.listMessages
As the error defined that particular data or props you are passing are not an object.
console.log(this.props.listMessagesQuery.listMessages)
check if you find it in current formate. If you don't find anything share you this console.log result. Hope it helps you
you are not sending listMessagesQuery.listMessages as a props to Chat.js component you are only sending name and userId as props to Chat component
your existing code in App.js
<Chat name={name} userId={userId} />
you need to send
<Chat name={name} userId={userId} listMessagesQuery={}/>

Resources