Use hook in export function - React Native - reactjs

I want to write 1 common function that I will use in different functional components.
This generic function uses hooks and I get the error: Error: Invalid hook call. Hooks can only be called inside the body of a functional component.
Example of my code:
App.js
import React from 'react';
import {
Text,
TouchableOpacity,
} from 'react-native';
import { Change } from 'static/Change';
export default function App() {
return (
<TouchableOpacity
onPress={() => {
Change();
}}
>
<Text>Click Me!</Text>
</TouchableOpacity>
);
}
Change.js
import React from 'react';
export const Change = () => {
const [State, setState] = React.useState(0);
// Other hook work.
// The function returns nothing
};
What is my mistake and how can I fix it?

Hooks has some rules to follow - https://reactjs.org/docs/hooks-rules.html
Refactor code as below
import React from "react";
import { Text, TouchableOpacity } from "react-native";
function useChange() {
const [state, setState] = React.useState(0);
function change(value) {
setState(value);
}
return { change, state };
}
export default function App() {
const { change, state } = useChange();
return (
<TouchableOpacity
onPress={() => {
// Update state value on press
change(state + 1);
}}
>
<Text>Click Me!{state}</Text>
</TouchableOpacity>
);
}

Related

React Native : Rendered more hooks than during the previous render

I am getting this error after implementing the gethomeid useState and cant understand I tried different things but nothing worked
import React, {useEffect, useState} from 'react';
import {
StyleSheet,
View,
Image,
Text,
TouchableOpacity,
Button,
} from 'react-native';
import {windowWidth, windowHeight} from '../utils/Dimentions';
//components
import EmptyContainer from '../components/EmptyContainer';
import Header from '../components/Header';
import TopTabs from '../components/Tabs';
//redux
import {getHomeData} from '../action/homedata';
import {connect} from 'react-redux';
import propTypes from 'prop-types';
// to render empty container
const Home = ({getHomeData, homeState, userDetails}) => {
const [gethomeid, setGethomeid] = useState(null);
if (userDetails) {
useEffect(() => {
getHomeData('123456');
}, []);
}
if (homeState.data != null && userDetails && gethomeid != null) {
console.log(homeState.data);
return (
<View style={{flex: 1}}>
<Header username={userDetails.name.split(' ')[0]} />
<TopTabs roomdata={homeState.data.roomMapper} />
</View>
);
} else {
return (
<View>
<EmptyContainer />
</View>
);
}
};
const mapStateToProps = state => ({
homeState: state.data,
userDetails: state.auth.user,
});
const mapDispatchToProps = {
getHomeData,
};
Home.propTypes = {
getHomeData: propTypes.func.isRequired,
homeState: propTypes.object.isRequired,
userDetails: propTypes.object,
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
I am trying to load 2 buttons that will pass the data on clicked to the gethomeid but this error i am not able to solve
THE SNAPSHOT OF THE ERROR:-
You are conditionally calling the useEffect hook. This breaks the rules of hooks. You can likely move the conditional into the hook callback.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, []);
You should probably also add any missing dependencies the linter may complain about, like userDetails and getHomeData.
useEffect(() => {
if (userDetails) {
getHomeData('123456');
}
}, [getHomeData, userDetails]);

React useSelector not working, what am I doing wrong?

I have a selectors folder with the selector.js file
const isRecipeFavorited = state => recipeId => {
const recipe = state.find(recipe => recipe.id === recipeId)
console.log(`Selector isRecipeFavourtied: ${recipeId}`)
return recipe ? recipe.is_fav : false
}
This is my favicon.js file which calls this useSelector.
import React, {useState, useEffect} from 'react'
import { FontAwesome } from '#expo/vector-icons'
import { View, TouchableOpacity,StyleSheet, Text, Alert } from 'react-native'
import {isRecipeFavorited} from '../selectors/selectors'
import {useSelector} from 'react-redux'
import { favids } from '../reducers/favids'
const FavIcon = ({recipeid, onToggle, isFavourite, text}) => {
const isFavNew = useSelector(isRecipeFavorited(recipeid))
return (
<View>
<TouchableOpacity style={styles.favItem} onPress={() => onToggle(recipeid)}>
<FontAwesome name={isFavNew === true ? 'heart' : 'heart-o'} size={40} color='red' />
<Text> {text}</Text>
</TouchableOpacity>
</View>
)
}
export default FavIcon
I keep getting an error saying my selector is not a function. _selectors.isRecipeFavourited is not a function.
I am trying to retrieve the value of the recipe.is_fav from the state.
I am also using Redux in this project.
I think you need to reverse state and recipeId in your selector function, i.e.:
const isRecipeFavorited = recipeId => state => {...}
This is a valid useSelector:
const thing = useSelector(state => state.thing);
In your case, you're calling a function with another type of argument, which in turn should return a function of the type that useSelector is expecting.
This is solved by drilling down into the favids object.
The selector function has been modified as below.
const recipe = state.favids.find(recipe => recipe.recipeid === recipeId)
return recipe ? recipe.is_fav : false
}```

My react hooks fail when used inside my react component that is deeply nesed into my react application

When I use useHistory hook here ...
import React from 'react';
import {View,Image,StyleSheet, TouchableOpacity} from 'react-native';
import theme from '../theme';
import Text from './Text'
import {useQuery} from '#apollo/react-hooks'
import {GET_REPOSITORY} from '../graphql/queries'
import {useHistory} from 'react-router-native'
const RepositoryItem = (props) => {
let history = useHistory()
let item = props.item
const styles = StyleSheet.create({
...
});
const reduceToK = (num) => {
if (num > 999) {
const thousandsNum = Math.round(num / 1000);
const remainerNum = Math.round((num % 1000) / 100);
return `${thousandsNum}.${remainerNum}k`;
} else return num;
};
return (
<View style={styles.rootContainer}>
{...}
</View>
);
};
export default RepositoryItem;
The application breaks and returns the following 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: 1. You might have mismatching versions of React and the renderer (such as React DOM)2. You might be breaking the Rules of Hooks3. You might have more than one copy of React in the same app
This component (RepositoryItem) is being used inside another component (RepositoryList) like so ...
import React from 'react';
import { FlatList, View, StyleSheet, Text } from 'react-native';
import RepositoryItem from './RepositoryItem';
// ...
const styles = StyleSheet.create({
...
});
const RepositoryList = () => {
let history = useHistory()
const repositories = useQuery(GET_REPOSITORIES, {fetchPolicy: 'cache-and-network'});
if (!repositories.loading) {
const ItemSeparator = () => <View style={styles.separator} />;
// Get the nodes from the edges array
const repositoryNodes = repositories.data.repositories && !repositories.loading
? repositories.data.repositories.edges.map(edge => edge.node)
: [];
return (
<FlatList
data={repositoryNodes}
ItemSeparatorComponent={ItemSeparator}
renderItem={RepositoryItem}
keyExtractor={repository => repository.fullName}
/>
);
} else {
return (<View><Text>Loading...</Text></View>);
}
};
export default RepositoryList;
That component is being used in the App component.
My theory is that in react native's FlatList renderItem prop one cannot use hooks.
Any idea what the issue might be here?
You may want to do it this way because renderItem accepts a function.
<FlatList
data={repositoryNodes}
ItemSeparatorComponent={ItemSeparator}
renderItem={({item}) => <RepositoryItem item={item} />}
keyExtractor={repository => repository.fullName}
/>
change
import {useHistory} from 'react-router-native'
t0
import {useHistory} from 'react-router-dom'
based on react-router-native documentation

React native typescript: usecontext functions not firing from inside child component

I have an issue when i try to use functions from a context inside a child component in a React native android app.
Below is my code for the context, and the form component im using it in (stripped down for brevity).
The "isFormOpen" object can be read no problem from inside any children that is wrapped in the provider, but when i try to call the "toggleForm" function from the same child component, it does nothing, no console errors either.
I have another context which is identical in structure and syntax except for vairable and function names etc, and that works perfectly, so im a bit confused as to why this does not work. I removed the other context, thinking there might be some type of conflict, but didnt solve it.
AccountContext.tsx
import React, { FC, createContext, useContext, useState } from 'react';
interface AccountContextType {
isFormOpen: boolean,
toggleForm: (toggle: boolean) => void
};
export const AccountContext = createContext<AccountContextType>({
isFormOpen: false,
toggleForm: () => null
});
export const AccountContextProvider: FC = props => {
const [formOpen, setFormOpen] = useState<boolean>(false);
const toggleForm = (toggle: boolean) => {
setFormOpen(toggle);
}
const value: AccountContextType = {
isFormOpen: formOpen,
toggleForm
}
return (
<AccountContext.Provider value={value}>
{props.children}
</AccountContext.Provider>
)
}
export const useAccountContext = () => useContext(AccountContext);
TrackUploadForm.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import { Button } from 'react-native-paper';
import { useAccountContext } from '../contexts/AccountContext';
import { AccountContextProvider } from '../contexts/AccountContext';
const TrackUploadForm = () => {
const accountContext = useAccountContext();
return (
<AccountContextProvider>
<SafeAreaView>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</SafeAreaView>
</AccountContextProvider>
)
};
export default TrackUploadForm;
useAccountContext is called outside the provider
export default function App() {
return (
<AccountContextProvider>
<Content />
</AccountContextProvider>
);
}
const Content = () => {
const accountContext = useAccountContext();
return (
<div className="App">
<h1>{accountContext.isFormOpen ? "true" : "false"}</h1>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</div>
);
};
accountContext.toggleForm(false) <-- always false, change it to accountContext.toggleForm(!accountContext.isFormOpen)
Together we have
https://codesandbox.io/s/cranky-panini-yo129

React Native save change switch with Async Storage

Im new in React Native, I have a problem with Switch, I want to save changes, dark mode and Switch, when I turn off the app and come back my changes should be saved. When I close the app, my switch came back to first position and dark mode does not work. I know that Im doing something wrong, but I did not mobile app and this is my first time and I dont know how to use AsyncStorage in this App to work this. Can somebody help me solve this problem?
import React, { createContext, useState, useEffect } from 'react';
import { AsyncStorage } from 'react-native';
export const DarkModeContext = createContext();
export default function DarkModeContextProvider(props) {
const [switchMode, setSwitchMode] = useState(false);
useEffect(() => {
let switch1 = switchMode;
AsyncStorage.setItem('switch1', JSON.stringify(switch1));
});
const SwitchThis = () => {
setSwitchMode(!switchMode);
};
return (
<DarkModeContext.Provider
value={{
switchMode,
SwitchThis
}}
>
{props.children}
</DarkModeContext.Provider>
);
}
and next component:
import React, { useState, useContext } from 'react';
import { View, ScrollView, TouchableOpacity, Text, AsyncStorage } from 'react-native';
import { List } from 'react-native-paper';
import BackgroundImage from './BackgroundImage';
import Clock from './Clock';
import TabIcon from './TabIcon';
import AddButton from './AddButton';
import { DarkModeContext } from './app-context';
const HomeScreen = () => {
const { switchMode } = useContext(DarkModeContext);
displayData = async () => {
try {
let switch1 = await AsyncStorage.getItem('switch1', function (err, switch1) {
JSON.parse(switch1)
}
)
return switch1
}
catch (error) {
return error
}
}
return (
<View
style={{
flex: 1,
backgroundColor: !switchMode ? 'white' : '#353535'
}}
>
<BackgroundImage fabButton={<AddButton/>}>
<Clock />
</BackgroundImage>
<ScrollView>
<List.Section>
<List.Subheader style={{ color: !switchMode ? 'black' : 'white' }}>
Task List
</List.Subheader>
<TouchableOpacity onPress={displayData}>
<Text>Click displayData</Text>
</TouchableOpacity>
</List.Section>
</ScrollView>
</View>
);
};
You are importing AsyncStorage from 'react-native' which is deprecated
use #react-native-community/react-native-async-storage
npm i #react-native-community/react-native-async-storage
And on your home screen you are not calling the function displayData() so how is data supposed to be displayed without function call.
and i do suggest making separate functions for writing and reading from async storage, it will help you reduce your code and time.
Like this:
let storeData=(name, obj)=> {
return new Promise((resolve,reject)=>{
let jsonOfItem = JSON.stringify(obj)
AsyncStorage.setItem(name, jsonOfItem).then(r=>resolve(jsonOfItem))
.catch(e=>reject(e))
})
}
let readData=(name)=> {
return new Promise((resolve,reject)=>{
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value
AsyncStorage.getItem(name).then(r=> resolve(JSON.parse(r)) )
.catch(e=>reject(e))
})
}
//Now you can just read write easily in async function like this:
let fetchedData = await readData("key")
//and for storing data.
storeData("key",object)

Resources