Accessing Refs in React Functional Component - reactjs

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>
);
}

Related

every time the screen switches, all the states reset (useContect)

I have a react native appication. It is a mockup of zillow. I created a useContext object that stores several of the different useState to store different information I want the home page and all of its children components to have access to.
From the home page, the user can click on the sort button and it redirects the user to a different page so that he can change how the information is sorted.
when the user clicks on a sort option, I have it os that it goes back to the home page by using navigation.goBack(). The issue is that every time the screen changes, the entire useContect object reverts to the original state that is set by default.
I need to figure out how to get the state in useContect to not revert to default when I change screens.
I tried scoping the useContext.provider to wrap the whole navigation stack to see if that would change anything but it did not work. Does anyone know a way to do this so that I can use different screens without reseting the entire useContext
Here is the HomeScreen:
import React, { useState, createContext, useEffect} from 'react'
import {View, Text, StyleSheet} from 'react-native'
import SearchAndFilterComponent from '../components/HomeScreen/SearchAndFilterComponent';
import ResultsComponent from '../components/HomeScreen/ResultsComponent';
import { SearchFilterSortContextProvider } from '../context/SearchFilterSortContext';
const HomeScreen = () => {
return (
<SearchFilterSortContextProvider>
<View style={styles.screen}>
<SearchAndFilterComponent/>
<View style={styles.fullSplit}></View>
<ResultsComponent />
</View>
</SearchFilterSortContextProvider>
)
}
const styles = StyleSheet.create({
fullSplit: {
width: '100%',
height: 2,
backgroundColor: 'black'
}
})
export default HomeScreen
SearchFilterSortContext.js:
import { useState, createContext, useEffect } from "react";
export const SearchFilterSortContext = createContext(null)
export const SearchFilterSortContextProvider = ({children}) => {
// General Search State
const [currentSearch, setCurrentSearch] = useState('')
const [activeSearch, setActiveSearch] = useState('')
const [sort, setSort] = useState('')
const [filter, sestFilter] = useState()
const [results, setResults] = useState([])
const [resultCounter, setResultCounter] = useState(0)
const [page, setPage] = useState(1)
const [totalPages, setTotalPages] = useState(0)
const [resultView, setResultView] = useState('List')
// Filter Home Type States
const [house, setHouse] = useState(true)
const [apartment, setApartment] = useState(false)
const [condos, setCondos] = useState(false)
const [townHouse, setTownHouse] = useState(false)
const [multiFamily, setMultiFamily] = useState(false)
const [manufactured, setManufactured] = useState(false)
const [land, setLand] = useState(false)
// Filter Home Details States
const [priceMin, setPriceMin] = useState(0)
...
// data management state
const [loadingMain, setLoadingMain] = useState(false)
return (
<SearchFilterSortContext.Provider value={{house, setHouse,
apartment, setApartment,
condos, setCondos,
townHouse, setTownHouse,
multiFamily, setMultiFamily,
manufactured, setManufactured,
land, setLand,
...
loadingMain, setLoadingMain}}>
{children}
</SearchFilterSortContext.Provider>
)
}
SortSelectionScreen.js:
import React, { useContext } from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import { Feather } from '#expo/vector-icons'
import { useNavigation } from '#react-navigation/native'
import { SearchFilterSortContext } from '../../context/SearchFilterSortContext'
const SortOptionsComponent = () => {
const {sort, setSort} = useContext(SearchFilterSortContext)
const navigation = useNavigation()
return (
<View>
<TouchableOpacity onPress={() => {setSort('Homes_for_You'); navigation.goBack()}} style={styles.row}>
<Text style={styles.text}>Homes For You</Text>
{
sort === 'Homes_for_You' ? <Feather name='check' color={'black'} size={20}/> : null
}
</TouchableOpacity>
<TouchableOpacity onPress={() => {setSort('Newest'); navigation.goBack()}} style={styles.row}>
<Text style={styles.text}>Newest</Text>
{
sort === 'Newest' ? <Feather name='check' color={'black'} size={20}/> : null
}
</TouchableOpacity>
...
</View>
)
}
Component that renders the results
ResultsComponent.js:
import React, { useEffect, useContext } from 'react'
import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native'
import axios from 'axios'
import { useNavigation } from '#react-navigation/native'
import { extendedPropertOptions } from '../../../zillow'
import { SearchFilterSortContext } from '../../context/SearchFilterSortContext'
import LoadingComponent from '../GeneralComponents.js/LoadingComponent'
import PropertyTile from './PropertyTile'
const ResultsComponent = () => {
const navigation = useNavigation()
const {results, setResults} = useContext(SearchFilterSortContext)
const {setResultCounter} = useContext(SearchFilterSortContext)
const {setTotalPages} = useContext(SearchFilterSortContext)
const {sort, setCurrentSearch} = useContext(SearchFilterSortContext)
const {loadingMain, setLoadingMain} = useContext(SearchFilterSortContext)
const {currentSearch, activeSearch} = useContext(SearchFilterSortContext)
const getDefaultResults = () => {
setLoadingMain(true)
extendedPropertOptions.params.location = 'Los Angeles, CA'
axios.request(extendedPropertOptions)
.then((response) => {
setResults(response.data.props)
setResultCounter(response.data.totalResultCount)
setTotalPages(response.data.totalPages)
setLoadingMain(false)
})
.catch((error) => {
console.error(error)
})
}
// apply search with all the filters
const getFilteredResults = () => {
console.log('fire search')
console.log(sort)
setLoadingMain(true)
{
activeSearch === '' ?
currentSearch === '' ?
extendedPropertOptions.params.location = 'Los Angeles, CA'
: extendedPropertOptions.params.location = currentSearch
: extendedPropertOptions.params.location = activeSearch
}
extendedPropertOptions.params.sort = sort
console.log(extendedPropertOptions)
axios.request(extendedPropertOptions)
.then((response) => {
setResults(response.data.props)
setResultCounter(response.data.totalResultCount)
setTotalPages(response.data.totalPages)
setCurrentSearch('')
setLoadingMain(false)
})
.catch((error) => {
console.error(error)
})
}
useEffect(() => {
getDefaultResults()
}, [])
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
getFilteredResults()
})
return unsubscribe
}, [navigation])
return (
<ScrollView style={styles.scroll}>
{
loadingMain === true ? <View><LoadingComponent /></View> : <View>{results.map((item) => {return(<View key={item.zpid}><PropertyTile item={item}/></View>)})}</View>
}
</ScrollView>
)
}
when I go back to the results component, and it runs getFilteredResults the sort has reverted back to ''. That is what I need to fix.

How to convert methods from Library to fit functional component

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

My SetState is erasing my previous content

I return this content from my ReviewPictures.tsx screen.
My problem is when i take a new Photo with my Photo Button
My SetState erase the previous Photos List
The Code :
ReviewPictures.tsx : Here i return my photos and my PhotoIcons Button
which can retake a photo if the user want so.
import {NavigationProp, RouteProp, useNavigation, useRoute} from '#react-navigation/native'; import React, {useContext, useEffect, useState} from 'react'; import {Image, StyleSheet, Text, View, ScrollView} from 'react-native'; import {Button} from 'react-native-paper'; import AnalyseContext from '../../contexts/Photoshoot/AnalyseContext'; import {Step, StepAlias} from '../../domain/photoShoot/Step'; import {PhotoshootStackParamList} from '../../navigation/PhotoshootNavigator'; import {IconButton, Colors} from 'react-native-paper'; import I18n from 'i18n-js'; import {AppStackParamList} from '../../navigation/AppNavigator'; import {OnboardedStackParamList} from '../../navigation/OnboardedNavigator';
const styles = StyleSheet.create({ image: {
width: 150,
height: 150, }, container: {
flex: 1, }, box: {height: 250}, row: {flexDirection: 'column', alignItems: 'center'}, });
const ReviewPictures: React.FC = () => {
const {params} = useRoute<RouteProp<OnboardedStackParamList, 'Photoshoot'>>();
const {picturePaths} = useContext(AnalyseContext);
const [content, setContent] = useState<JSX.Element[]>([]);
const navigation = useNavigation<NavigationProp<PhotoshootStackParamList>>();
const Appnavigation = useNavigation<NavigationProp<AppStackParamList>>();
const toRedo = !params?.redo;
useEffect(() => {
setContent(setPageContent());
}, []);
const setPageContent = () => {
const c: JSX.Element[] = [];
picturePaths.forEach((value: string, step: StepAlias) => {
console.log(value);
c.push(
<View style={[styles.box, styles.row]}>
<Text>{I18n.t(`${step}`)}</Text>
<Image style={styles.image} source={{uri: value}} />
<IconButton
icon="camera"
color={Colors.blue500}
size={40}
onPress={() => {
console.log('Pressed');
Appnavigation.navigate('Logged', {
screen: 'Onboarded',
params: {screen: 'Photoshoot', params: {redo: toRedo, onboarded: false, review: step}},
});
}}
/>
</View>,
);
});
return c; };
return (
<>
<ScrollView style={styles.container}>{content}</ScrollView>
<Button onPress={() => navigation.navigate('Mileage')}>Valider</Button>
</> ); };
export default ReviewPictures;
And Photoshoot.tsx which take and store the photos :
import {RouteProp, useRoute} from '#react-navigation/native';
import I18n from 'i18n-js';
import React, {useContext, useEffect, useState} from 'react';
import {StatusBar} from 'react-native';
import Loading from '../../components/UI/loading/Loading';
import AnalysesContext from '../../contexts/Analyses/AnalysesContext';
import {UserContext} from '../../contexts/User/UserContext';
import {ONBOARDING_SCENARIO, WEAR_SCENARIO} from '../../domain/photoShoot/Scenario';
import {fromStepAlias, Step} from '../../domain/photoShoot/Step';
import {OnboardedStackParamList} from '../../navigation/OnboardedNavigator';
import PhotoshootNavigator from '../../navigation/PhotoshootNavigator';
import {
createRetakeScenario,
extractReferenceTyresToRetake,
extractWearTyresToRetake,
} from '../../utils/retakeTyres.utils';
/**
* Wrap AnalyseWearNavigator.
* Ensures steps and/or referenceId are loaded before use of AnalyseWearNavigator.
* Meanwhile, it shows a waiting loader.
*
* We have to wait before mounting AnalyseWearNavigator. Otherwise, Navigator take configuration it had at first mount and don't care if you update state later.
*/
const Photoshoot = () => {
const {params} = useRoute<RouteProp<OnboardedStackParamList, 'Photoshoot'>>();
const {user, vehicleId} = useContext(UserContext);
const {wear, reference, fetchingAnalysis} = useContext(AnalysesContext);
const [isLoading, setIsLoading] = useState(true);
const [referenceId, setReferenceId] = useState<number>();
const [steps, setSteps] = useState<Step[]>([]);
useEffect(() => {
if (!fetchingAnalysis) {
if (user && vehicleId) {
if (params?.redo) {
loadRetakeScenario();
}
if (params?.review) {
console.log('Joué ' + params?.review);
loadReviewScenario();
} else {
loadScenario();
}
}
}
}, [user, vehicleId, wear, reference, params, fetchingAnalysis]);
const loadReviewScenario = () => {
if (params?.review) {
setSteps([fromStepAlias(params.review)]);
setIsLoading(false);
// HERE THE PROBLEM WITH SETSTEPS
}
};
const loadRetakeScenario = () => {
const wearTyresToRetake = extractWearTyresToRetake(wear?.tyreWears);
const referenceTyresToRetake = extractReferenceTyresToRetake(reference?.tyreReferences);
const scenario = createRetakeScenario(wearTyresToRetake, referenceTyresToRetake);
setSteps(scenario);
setIsLoading(false);
};
const loadScenario = async () => {
setReferenceId(reference?.id);
setSteps(!!params?.onboarded ? WEAR_SCENARIO : ONBOARDING_SCENARIO);
setIsLoading(false);
};
const content = isLoading ? (
<Loading waitingText={I18n.t('ANALYSIS_PAGE.LOADING_MESSAGE')} />
) : (
<>
<StatusBar barStyle="dark-content" />
<PhotoshootNavigator steps={steps} referenceId={referenceId} redo={!!params?.redo} />
</>
);
return content;
};
export default Photoshoot;
Step.ts which guarantee the position of the tyre, it's a Tyre Recognition app :
import {PhotoType} from '../../models/PhotoType';
import {TyrePosition} from '../TyrePosition';
/** Step of a photo shoot */
export type Step = {
tyre: TyrePosition;
whichSideToTake: PhotoType;
};
export type StepAlias = `${TyrePosition}_${PhotoType}`;
export const toStepAlias = (step: Step): StepAlias => {
return `${step.tyre}_${step.whichSideToTake}`;
};
export const fromStepAlias = (stepAlias: StepAlias): Step => {
console.log(stepAlias.split('_'));
const split = stepAlias.split('_');
return {tyre: split[0] as TyrePosition, whichSideToTake: split[1] as PhotoType};
};
What's wrong with setStep ?
From what I can understand of your post, you are having some issue with the step state and updating it. In the three places in Photoshoot.tsx file where you enqueue any steps state updates you should probably use a functional state update to shallowly copy and update from any previously existing state instead of fully replacing it.
Example:
const loadReviewScenario = () => {
if (params?.review) {
setSteps(steps => [...steps, fromStepAlias(params.review)]);
setIsLoading(false);
}
};
const loadRetakeScenario = () => {
const wearTyresToRetake = extractWearTyresToRetake(wear?.tyreWears);
const referenceTyresToRetake = extractReferenceTyresToRetake(reference?.tyreReferences);
const scenario = createRetakeScenario(wearTyresToRetake, referenceTyresToRetake);
setSteps(steps => [...steps, scenario]);
setIsLoading(false);
};
const loadScenario = async () => {
setReferenceId(reference?.id);
setSteps(steps => [
...steps,
!!params?.onboarded ? WEAR_SCENARIO : ONBOARDING_SCENARIO
]);
setIsLoading(false);
};

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

ref.current is always return null with react hooks when i use flatList

the error always occurred when i pass ref to a renderItem Element which passed to flatList component, what i want here is to manipulate the item itself with refs but i always get this error
here is my code snippet
import React, { useState, useRef, useEffect } from 'react';
import { View, Text, FlatList, Button } from 'react-native';
import Animated, { Transitioning, Transition } from 'react-native-reanimated';
import PropTypes from 'prop-types';
import styles from './styles';
const AnimatedHorizontalList = ({ data }) => {
const [list, setList] = useState([]);
const ref = useRef();
const transition = (
<Transition.Sequence>
<Transition.Out delayMs={200} type="scale" />
<Transition.Change delayMs={200} interpolation="easeInOut" />
<Transition.In delayMs={2000} type="fade" />
</Transition.Sequence>
);
useEffect(() => {
ref.current.animateNextTransition();
setList(data);
});
const renderItem = ({ item }) => {
return (
<Transitioning.View ref={ref} transition={transition}>
<View>
<Text>{item}</Text>
</View>
</Transitioning.View>
);
};
return (
<View style={styles.container}>
<FlatList horizontal data={list} renderItem={renderItem} />
</View>
);
};
AnimatedHorizontalList.propTypes = {};
export default AnimatedHorizontalList;
When you use useRef() on line const ref = useRef();, you can start ref with a default value, so you can write const ref = useRef({ animateNextTransition: () => {} });, that way it dont break on line ref.current.animateNextTransition();

Resources