Can't figure out why React Hook is invalid - react-native

I'm trying to use useContext hook in a React Native page I have, I'm using this hook in other components as well and they work just fine, but this one won't - can't figure out why.
This is my component:
const GoalCard = (goal: IGoal) => {
const {theme} = useContext(ThemeContext); <-- error is here
return (
<Animated.View style={[styles.card, {height: cardHeight, backgroundColor: "#ff5e"}]}>
<View>
.....
</View>
</Animated.View>
);
I'm using this component inside a FlatList which is in a separate file.
return (
<SafeAreaView style={[styles.pageContainer, {backgroundColor: theme.background}]}>
<FlatList
showsVerticalScrollIndicator={false}
style={{padding: 8}}
data={goals}
renderItem={({index}) => GoalCard(goals[index])}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
I get the error: Invalid hook call, but it does not seem like something is wrong here.
Saw few questions about this, but none worked, also, nothing seems wrong according to the Rules of Hooks page. Why this error happens here?
EDIT:
this is my ThemeProvider:
function ThemeProvider({ children }) {
const [dark, setDark] = React.useState(false);
const toggle = () => {
setDark(!dark);
}
const theme = dark ? themes.dark : themes.light;
return (
<ThemeContext.Provider value={{theme, dark, toggle}}>
{children}
</ThemeContext.Provider>
)
}
And this is my App.tsx:
<ThemeProvider>
<Provider store={store}>
<NavigationContainer>
.....
</NavigationContainer>
</Provider>
</ThemeProvider>

Related

Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance

This is my first Apollo Project, but I have been working with it for awhile, and things had been pretty smooth in terms of Apollo, until recently.
While I understand how Apollo works, I obviously do not know all the minutae involved with it, and I am unclear I suppose on exactly how the project is supposed to be wrapped.
Below is my App.js, when it worked as anticipated...
// Auth for token
const authLink = setContext((_, { headers }) => {
const token = state
return {
headers: {
...headers,
authorization: token ? `${token}` : ''
}
}
})
// Initialize Apollo Client
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
});
const Stack = createNativeStackNavigator();
export default function App() {
// Other irrelevant things like fonts...
const ErrorPrompt = (props) => {
return(
<View>
<View style={{height: 50, width: '100%', backgroundColor: "#534FFF"}}/>
<Text style={{...Template.title, fontSize: 30}}>Something happened!</Text>
<Text style={{...Template.subTitle, fontSize: 20}}>We apologize for the inconvenience</Text>
<Text style={{...Template.title, fontSize: 20, color: '#534FFF'}}>{props.error.toString()}</Text>
<View style={{marginLeft: 30}}>
<Text style={{...Template.subTitle, fontSize: 15, marginRight: 30, marginLeft: 0}}>An email has been sent to the Support Team to attempt to prevent this error from occurring again. We thank you for your support and patience</Text>
</View>
<View style={{marginTop: 50}}>
<Button onPress={props.resetError} title={'Reload Application'} />
</View>
</View>
)
}
if(!loaded){
return null
}
try{
return (
<ErrorBoundary
FallbackComponent={ErrorPrompt}
>
<NavigationContainer>
<ApolloProvider client={client}>
<RecoilRoot>
<IconRegistry icons={EvaIconsPack} />
<ApplicationProvider {...eva} theme={{...eva.light, ...theme}}>
<PaperProvider>
<KeyboardAvoidingView
behavior="padding"
enabled
style={{flexGrow:1,height:'110%'}}
>
<View style={AppStyles.container}>
<Stack.Navigator screenOptions={{headerShown: false}}>
{/* {loggedIn === false ? ( */}
<Stack.Screen name="/">
{props => <LandingPage {...props} handleLoggedIn={handleLoggedIn} rememberMe={rememberMe} setRememberMe={setRememberMe} />}
</Stack.Screen>
{/* ) : null} */}
<Stack.Screen name="home">
{props => <Home {...props} handleLoggedIn={handleLoggedIn} />}
</Stack.Screen>
{/* About 40 more Stack Screens */}
</Stack.Navigator>
</View>
</KeyboardAvoidingView>
</PaperProvider>
</ApplicationProvider>
</RecoilRoot>
</ApolloProvider>
</NavigationContainer>
</ErrorBoundary>
)
}
catch(error){
return(<ErrorPrompt code={error} />)
}
I apologize for the amount of code, but my primary question is, does the order of the wrapping components matter? I know I need my NavContainer, ErrorBoundary, and RecoilRoot wrapping all of the pages, but do the orders of these matter? Because the code I have about was working perfectly until I added a mutation to the ErrorPrompt Component. The new ErrorPrompt looked like this...
const ErrorPrompt = (props) => {
useEffect(() => {
sendErrorEmail({
variables: {
errorCode: props.error.toString()
}
})
}, [])
return(
// The same return as before
)
}
After this addition, I was given the Could not find "client" in the context or passed in as an option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient instance error. The message isn't particularly helpful, as both everything is wrapped in AND I have a client passed in. Does anyone know the solution / if order of these wrappings matters, and it it does, what order it should be in?
I found the issue. It was not a matter of the order in which things were wrapped but rather that I had the declaration for the sendErrorEmail mutation OUTSIDE of a component that was wrapped with the Apollo Provider. I had it out floating in App.js, but when I moved the line to under ErrorPrompt = () => { the error was removed

nothing was returned from render but can't be fixed

I don't know what I did wrong in this function, I thought I had a return but I don't think it's being considered, and when I change the closing brackets or parenthesis it shows an error :/
Can someone help me?
function PopSheet() {
let popupRef = React.createRef()
const sheet =() => {
const onShowPopup =()=> {
popupRef.show()
}
const onClosePopup =()=> {
popupRef.close()
}
return (
<SafeAreaView style ={styles.container}>
<TouchableWithoutFeedback onPress={onShowPopup}>
</TouchableWithoutFeedback>
<BottomPopup
title="Demo Popup"
ref ={(target) => popupRef =target}/>
onTouchOutside={onClosePopup}
</SafeAreaView>
);
}
};
You need to use the render function which is responsible for rendering or display the JSX element into your app.
render() {
return (
<SafeAreaView style={styles.container}>
<TouchableWithoutFeedback
onPress={onShowPopup}></TouchableWithoutFeedback>
<BottomPopup title="Demo Popup" ref={target => (popupRef = target)} />
onTouchOutside={onClosePopup}
</SafeAreaView>
);
}

useEffect not working in custom drawer component without refresh

So I am using react-navigation 5 and I have a custom drawer component for my app. I want to display the name of the logged-in user in the drawer for which I am using a state variable and I am updating the state from firestore. I am calling a function in useEffect which accesses firestore and gets the name of the user. But I think the useEffect is not working without refresh because unless I save the project and refresh the application the state is not getting updated in the application and I cannot see the name of the user without refreshing but it is visible after a refresh. Any ideas why this is happening? Any help would be appreciated. Thank you.
Custom drawer
export default function CustomDrawer(props) {
const paperTheme = useTheme();
const [name,setName]=useState('');
useEffect(() => {
doStuff();
}, []);
const doStuff = async () => {
var phone=global.phone;
await firestore().collection("Users").where('Phone Number', '==', phone).get().then(querySnapshot=>{
querySnapshot.forEach(documentSnapshot => {
console.log("in drawer");
console.log(documentSnapshot.data());
setName(documentSnapshot.data().Name);
})
})
};
return(
<View style={{flex:1}}>
<DrawerContentScrollView {...props}>
<View style={styles.drawerContent}>
<View style={styles.userInfoSection}>
<View style={{flexDirection:'row',marginTop: 15}}>
<Avatar.Image
source={{
uri: ''
}}
size={50}
/>
<View style={{marginLeft:15, flexDirection:'column'}}>
<Title style={styles.title}>{name}</Title>
</View>
</View>
</View>
</View>
</DrawerContentScrollView>
</View>
);
}
Looks like you have doStuff function defined outside the useEffects.
Either you need to put it inside useEffects or add it in dependency list
useEffect(() => {
doStuff();
}, [doStuff]);

Flat list not showing anything even though data and render is provided

I have a flatlist inside a component like so:
const MyInvestments = (props) => {
return (
<SafeAreaView>
<Text style={styles.myInvestments}>
My Investments
</Text>
<FlatList
data={props.investments}
renderItem={renderInvestment}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
and props.investments is the following:
[{name:"investment1", id:"23423"},
{name:"investment2", id: "32423"}]
However, the flatlist items, the investment1 and investment2 are not getting displayed on the screen whereas they are supposed to show up as items of the flatlist. How to fix this?
renderInvestment looks like the following:
const renderInvestment = (props) => {
<View>
<Text>Investment</Text>
<Text>{props.name}</Text>
</View>
}
A couple of things you can try.
Use capitalized const for RenderInvestment
Return a JSX from RenderInvestment function like this:
const RenderInvestment = (props) => (
<View>
<Text>Investment</Text>
<Text>{props.name}</Text>
</View>
)

React Native, Passing Navigation to Stateless Flatlist component

Currently I have:
return(
<View style={{flex: 1, paddingTop:20}}>
<FlatList
data={this.state.dataSource}
renderItem={(data) => <EventCard eventinfo = {data.item} navigation=
{this.props.navigation}/>}
keyExtractor={(item) => item.eventname}
/>
and
const EventCard = ({eventinfo, navigation}) => {
return (
<TouchableOpacity style={{backgroundColor: 'transparent'}} onPress= {()
=> navigation.navigate('CurrRes')}>
I dont understand why Navigation cant be evaluated in my Eventcard, and navigation doesnt work. Any help would be appreciated.
(yes withnavigation is imported in the first file and the project runs but crashes when one of the flatlist items is pressed)
The error i get is
undefined is not an object (evaluating 'navigation.navigate')
Here is an example I found to explain it better.
You need to access props differently than just a normal function with deconstruction.
const Child = (props) => {
return (
<div style={{backgroundColor: props.eyeColor}} />
)
}
https://medium.com/#PhilipAndrews/react-how-to-access-props-in-a-functional-component-6bd4200b9e0b
Nevermind, I had to export the actual list with navigation so each of its nested stateless components could navigate. Sorry!