I got this unexpected error in React-Native - react-native

Error: Element type is invalid: expected a string (for built-in components) or
a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of App.
Here is the code of App.js
import React, { useState } from 'react';
import { StyleSheet, View } from 'react-native';
import * as Fonts from 'expo-font';
import { AppLoading } from 'expo-app-loading';
import Header from './components/Header';
import StartGameScreen from './screens/StartGameScreen';
import GameScreen from './screens/GameScreen';
import GameOverScreen from './screens/GameOverScreen';
const fetchFonts = () => {
return Fonts.loadAsync({
'open-sans': require('./assets/fonts/OpenSans-Regular.ttf'),
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf')
});
};
export default function App() {
const [userNumber, setUserNumber] = useState();
const [guessRounds, setGuessRounds] = useState(0);
const [dataLoaded, setDataLoaded] = useState(false);
if (!dataLoaded) {
return <AppLoading
startAsync={fetchFonts}
onFinish={() => setDataLoaded(true)}
onError={(err) => console.log(err)}
/>
}
const configureNewGameHandler = () => {
setGuessRounds(0);
setUserNumber(null);
};
const startGameHandler = selectedNumber => {
setUserNumber(selectedNumber);
};
const gameOverHandler = numOfRounds => {
setGuessRounds(numOfRounds);
};
let content = <StartGameScreen onStartGame={startGameHandler} />;
if (userNumber && guessRounds <= 0) {
content = (
<GameScreen userChoice={userNumber} onGameOver={gameOverHandler} />
);
} else if (guessRounds > 0) {
content = (
<GameOverScreen
roundsNumber={guessRounds}
userNumber={userNumber}
onRestart={configureNewGameHandler}
/>
);
}
return (
<View style={styles.screen}>
<Header title="Guess a Number" />
{content}
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1
}
});

Most likely you forgot to "export default" in one of these components
import Header from './components/Header';
import StartGameScreen from './screens/StartGameScreen';
import GameScreen from './screens/GameScreen';
import GameOverScreen from './screens/GameOverScreen';
If you are just "export"ing without "default", then use {} while importing

Related

How to use redux component in App.js React Native?

I'm doing a simple counter app. It has one label, and a button that you can increment by + 1 (each time it's pushed).
Using redux, I want to use the count that I store (in my Redux Store) in App.js file. However, I'm getting an error:
Error: could not find react-redux context value; please ensure the component is wrapped in a Provider
Using the useSelector works in other files, just not App.js. Is there a work around?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Dogs from './components/Dogs';
import { Provider, useSelector } from 'react-redux';
import store from './redux/configureStore'
export default function App() {
const count = useSelector((state) => state.counter.count);
{/*useSelector does not work in this file!*/}
return (
<Provider store={store}>
<View style={styles.container}>
<Text>{`ha ${count}`}</Text>
<Dogs />
</View>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Counter.js
import React, { useState, useEffect } from "react";
import { View, Text, StyleSheet, Button} from "react-native";
import { useDispatch, useSelector } from "react-redux";
import { increment } from '../redux/ducks/counter'
const Counter = () => {
const count = useSelector((state) => state.counter.count);
{/*useSelector works in this file!*/}
const dispatch = useDispatch();
const handleIncrement = () => {
dispatch(increment())
};
return (
<div>
{/* <Text>{` COunt: ${count}`}</Text> */}
<Button onPress={handleIncrement}>Increment</Button>
</div>
);
}
const styles = StyleSheet.create({})
export default Counter;
redux/configureStore.js
import { combineReducers, createStore } from 'redux';
import counterReducer from './ducks/counter';
const reducer = combineReducers({
counter: counterReducer
});
const store = createStore(reducer);
export default store;
redux/ducks/counter.js
const INCREMENT = 'increment';
export const increment = () => ({
type: INCREMENT
})
const initialState = {
count: 0
};
export default ( state = initialState, action) => {
switch(action.type) {
case INCREMENT:
return{...state, count: state.count + 1}
default:
return state;
}
};
As error saying, you are using useSelector out side of provider. In your app.js you are using useSelector before the app renders, so it is not able to find store. So, create a component for functionality which you want to use in app.js like this :
Create a file, call it anything like CountView.js, in CountView.js use your redux login :
CountView.js
import React from 'react';
import { Text } from 'react-native';
import { useSelector } from 'react-redux';
const CountView = () => {
const count = useSelector((state) => state.counter.count);
return (
<Text>{`ha ${count}`}</Text>
)
}
export default CountView;
Now, In your app.js use this component :
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Dogs from './components/Dogs';
import { Provider } from 'react-redux';
import store from './redux/configureStore'
import CountView from '../components/CountView'; // import CountView component
export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
{/* Use component here */}
<CountView />
<Dogs />
</View>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Keep other things as it is, and now your functionality will works.
useSelector will work only if you wrap it inside Provider. you can create a wrapper file for App.
const AppWrapper = () => {
return (
<Provider store={store}> // Set context
<App /> // Now App has access to context
</Provider>
)
}
In App.js
const App = () => {
const count = useSelector((state) => state.counter.count); // will Work!
}
Unlike a regular React application, an expo React-Native application is not wrapped using an index.js file. Therefore when we wrap the provider in app.js for a React-Native app, we wrap it in index.js for React application. So the hooks like useSelector or useDispatch run before the provider is initialized. So, I would suggest not using any hooks in the app component, instead, we can create other components in the app.js and use the hooks in a separate component like in the code I have used below.
const Root = () => {
const [appIsReady, setAppIsReady] = useState(false);
const dispatch = useDispatch();
const fetchToken = async () => {
const token = await AsyncStorage.getItem("token");
console.log("Stored Token: ", token);
if (token) {
dispatch(setAuthLogin({ isAuthenticated: true, token }));
}
};
const LoadFonts = async () => {
await useFonts();
};
useEffect(() => {
async function prepare() {
try {
await SplashScreen.preventAutoHideAsync();
await LoadFonts();
await fetchToken();
} catch (e) {
console.warn(e);
} finally {
setAppIsReady(true);
}
}
prepare();
}, []);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<NavigationContainer onReady={onLayoutRootView}>
<MainNavigation />
</NavigationContainer>
);
};
export default function App() {
return (
<>
<Provider store={store}>
<ExpoStatusBar style="auto" />
<Root />
</Provider>
</>
);
}

Fonts loading, but I need to use Font.loadAsync

I'm trying to add a custom font to ReactNative using expo
the fonts are actually showing up ok for now, but I keep getting a warning that I should use Font.loadAsync, I've tried to adapt what's in the docs to the tabbed template and watched a few tutorials, most recently Maximillian's on Udemy feels the closest.. but even though I'm using Font.loadAsync I'm still getting the same warning.
Repo, Tabs Template with Custom Fonts
import { StatusBar } from 'expo-status-bar';
import React, { useState } from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import useCachedResources from './hooks/useCachedResources';
import useColorScheme from './hooks/useColorScheme';
import Navigation from './navigation';
// expo install expo-app-loading
import AppLoading from 'expo-app-loading';
// import { AppLoading } from 'expo';
// import { useFonts } from 'expo-font';
import * as Font from 'expo-font';
const fetchFonts = () => {
return Font.loadAsync({
'CharterBold': require('./assets/fonts/CharterBold.otf'),
'CharterBoldItalic': require('./assets/fonts/CharterBoldItalic.otf'),
'CharterItalic': require('./assets/fonts/CharterItalic.otf'),
'CharterRegular': require('./assets/fonts/CharterRegular.otf'),
'SpaceMono': require('./assets/fonts/SpaceMono-Regular.ttf'),
})
}
export default function App() {
const isLoadingComplete = useCachedResources();
const colorScheme = useColorScheme();
const [fontsLoaded, setFontsLoaded] = useState(false)
// const [loaded] = useFonts({
// CharterBold: require('./assets/fonts/CharterBold.otf'),
// CharterBoldItalic: require('./assets/fonts/CharterBoldItalic.otf'),
// CharterItalic: require('./assets/fonts/CharterItalic.otf'),
// CharterRegular: require('./assets/fonts/CharterRegular.otf'),
// SpaceMono: require('./assets/fonts/SpaceMono-Regular.ttf'),
// });
if (!fontsLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => setFontsLoaded(true)}
onError={(err) => console.log(err)}
/>
);
}
if (!isLoadingComplete) {
return null;
} else {
return (
<SafeAreaProvider>
<Navigation colorScheme={colorScheme} />
<StatusBar />
</SafeAreaProvider>
);
}
}
Create a folder called hooks where your App.js is located.
Inside that create a file called useFonts.js
Your useFonts.js should look like this -
import * as Font from "expo-font";
const useFonts = async () => {
await Font.loadAsync({
CharterBold: require("./assets/fonts/CharterBold.otf"),
CharterBoldItalic: require("./assets/fonts/CharterBoldItalic.otf"),
CharterItalic: require("./assets/fonts/CharterItalic.otf"),
CharterRegular: require("./assets/fonts/CharterRegular.otf"),
SpaceMono: require("./assets/fonts/SpaceMono-Regular.ttf"),
});
}
export default useFonts;
Now your App.js should look like this -
import { StatusBar } from "expo-status-bar";
import React, { useState } from "react";
import { SafeAreaProvider } from "react-native-safe-area-context";
import useCachedResources from "./hooks/useCachedResources";
import useColorScheme from "./hooks/useColorScheme";
import Navigation from "./navigation";
import AppLoading from "expo-app-loading";
import useFonts from "./hooks/useFonts";
export default function App() {
const isLoadingComplete = useCachedResources();
const colorScheme = useColorScheme();
const [IsReady, SetIsReady] = useState(false);
const FetchFonts = async () => {
await useFonts();
};
if (!IsReady) {
return (
<AppLoading
startAsync={FetchFonts}
onFinish={() => SetIsReady(true)}
onError={(err) => console.log(err)}
/>
);
}
return (
<SafeAreaProvider>
<Navigation colorScheme={colorScheme} />
<StatusBar />
</SafeAreaProvider>
);
}
I found what I need under ./hooks/useCachedResources.ts
there is already a section that is using await Font.loadAsync
I just needed to add my fonts to that section.

react native memory leak react navigation

I want to check If the user has a secure Token in a useEffect but I get this error Message.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.
This happens when I use the useEffect. If I remove it, then I get no error message but I need to check if the user has the token.
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import getSecureKey from '../utilies/getSecureKey';
const Stack = createStackNavigator();
const AppStack = ({ navigation }) => {
useEffect(() => {
getSecureKey().then(res => console.log(res)).catch(e => console.log(e));
}, []);
return (
<Stack.Navigator showIcon={true} initialRouteName="AppTabs">
<Stack.Screen name="AppTabs" component={AppTabs} options={{headerTitle: () => <Header />, headerStyle: {
backgroundColor: '#fff'
}}} />
.....
getSecureToken:
import * as SecureStore from 'expo-secure-store';
const getSecureKey = async () => {
const key = await SecureStore.getItemAsync('jwt');
return key;
};
export default getSecureKey;
App.js
import React, { useState, useEffect } from 'react';
import * as Font from 'expo-font';
import { NavigationContainer } from '#react-navigation/native';
import AppLoading from 'expo-app-loading';
import { Provider } from 'react-redux';
import store from './src/redux/store/index';
import AppStack from './src/navigation/stack';
const getFonts = async () => {
await Font.loadAsync({
"nunito-regular": require("./assets/fonts/Nunito-Regular.ttf"),
"nunito-bold": require("./assets/fonts/Nunito-Bold.ttf"),
});
};
const App = () => {
const [fontsLoaded, setFontsLoaded] = useState(false);
if(fontsLoaded) {
return (
<Provider store={store}>
<NavigationContainer><AppStack /></NavigationContainer>
</Provider>)
} else {
return (<AppLoading startAsync={getFonts} onFinish={() => setFontsLoaded(true)} onError={() => {}} />)
}
};
export default App;
Don't restore token in the navigator. Instead do this -
Firstly, install expo-app-loading from here
Then, create a folder called navigation where your App.js is located. Then inside it create a File called AppNavigator.js.
Inside AppNavigator.js, paste this
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { createStackNavigator } from '#react-navigation/stack';
import getSecureKey from '../utilities/getSecureKey';
const Stack = createStackNavigator();
const AppNavigator = () => {
// Remove these Lines --
// useEffect(() => {
// getSecureKey()
// .then((res) => console.log(res))
// .catch((e) => console.log(e));
// }, []);
return (
<Stack.Navigator showIcon={true} initialRouteName="AppTabs">
<Stack.Screen
name="AppTabs"
component={AppTabs}
options={{
headerTitle: () => <Header />,
headerStyle: {
backgroundColor: '#fff',
},
}}
/>
</Stack.Navigator>
);
};
export default AppNavigator;
For your fonts create a folder called hooks where your App.js is located and inside that create a file useFonts.js
In useFonts.js write like this -
import * as Font from "expo-font";
export default useFonts = async () => {
await Font.loadAsync({
"nunito-regular": require("./assets/fonts/Nunito-Regular.ttf"),
"nunito-bold": require("./assets/fonts/Nunito-Bold.ttf"),
});
};
In your App.js
import React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import { NavigationContainer } from '#react-navigation/native';
import AppLoading from 'expo-app-loading';
import useFonts from "./hooks/useFonts";
import getSecureKey from './utilities/getSecureKey';
import AppNavigator from './navigation/AppNavigator';
export default function App() {
const [IsReady, SetIsReady] = useState(false);
// Always perform Token Restoration in App.js just to keep code clear.
const FontAndTokenRestoration = async () => {
await useFonts(); // Font is being loaded here
const token = await getSecureKey();
if (token) {
console.log(token);
}
};
if (!IsReady) {
return (
<AppLoading
startAsync={FontAndTokenRestoration}
onFinish={() => SetIsReady(true)}
onError={() => {}}
/>
);
}
return (
<NavigationContainer>
<AppNavigator />
</NavigationContainer>
);
}

Check the render method of `InstanceImage` while testing using testing-library/react-native

I am trying to test a functional component with Redux using testing-library/react-native.
//InstanceImage.component.js
export default InstanceImage = (props) => {
// <props init, code reduced >
const { deviceId, instanceId } = DeviceUtils.parseDeviceInstanceId(deviceInstanceId)
const deviceMap = useSelector(state => {
return CommonUtils.returnDefaultOnUndefined((state) => {
return state.deviceReducer.deviceMap
}, state, {})
})
const device = deviceMap[deviceId]
const instanceDetail = device.reported.instances[instanceId]
if (device.desired.instances[instanceId] !== undefined) {
return (
<View
height={height}
width={width}
>
<ActivityIndicator
testID={testID}
size={loadingIconSize}
color={fill}
/>
</View>
)
}
const CardImage = ImageUtils[instanceDetail.instanceImage] === undefined ? ImageUtils.custom : ImageUtils[instanceDetail.instanceImage]
return (
<CardImage
testID={testID}
height={height}
width={width}
fill={fill}
style={style}
/>
)
}
And the test file is
//InstanceImage.component.test.js
import React from 'react'
import { StyleSheet } from 'react-native';
import { color } from '../../../../src/utils/Color.utils';
import ConstantsUtils from '../../../../src/utils/Constants.utils';
import { default as Helper } from '../../../helpers/redux/device/UpdateDeviceInstanceIfLatestHelper';
import InstanceImage from '../../../../src/components/Switches/InstanceImage.component';
import { Provider } from 'react-redux';
import { render } from '#testing-library/react-native';
import configureMockStore from "redux-mock-store";
import TestConstants from '../../../../e2e/TestConstants';
const mockStore = configureMockStore();
const store = mockStore({
deviceReducer: {
deviceMap: Helper.getDeviceMap()
}
});
const currentState = ConstantsUtils.constant.OFF
const styles = StyleSheet.create({
// styles init
})
const props = {
//props init
}
describe('Render InstanceImage', () => {
it('InstanceImage renders correctly with values from props and Redux', () => {
const rendered = render(<Provider store={store}>
<InstanceImage {...props}/>
</Provider>)
const testComponent = rendered.getByTestId(TestConstants.icons.INSTANCE_IMAGE)
});
})
And when I run the test file it gives the following error
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of `InstanceImage`.
The component runs correctly when the app is run and there aren't any crashes. Still, this error occurs while performing tests.I am new to creating test cases for react native app so not able to debug this issue.
Found the error, the CardImage returns a custom svg image which was causing the issue. I used the jest-svg-transformer and it solved the issue.
Reference: https://stackoverflow.com/a/63042067/4795837

React Native Date Range

import React, {useState} from "react";
import { StyleSheet, View, Text } from "react-native";
import { globalStyles } from "../styles/global";
import {Calendar, CalendarList, Agenda} from 'react-native-calendars';
import {LocaleConfig} from 'react-native-calendars';
import moment from "moment";
import DateRangePicker from "react-native-daterange-picker";
export default function About(){
const [endDate, setendDate] = useState(null)
const [startDate, setstartDate] = useState(null)
const [displayedDate, setdisplayedDate] = useState(moment())
state = {
endDate: null,
startDate: null,
displayedDate: moment()
};
const handleSubmit = (props) => {
console.log(props);
setendDate(props.endDate);
setstartDate(props.startDate);
setdisplayedDate(props.displayedDate);
// console.log(props.startDate);
// console.log(props.displayedDate);
}
return(
<View style={globalStyles.container}>
<DateRangePicker
onChange={ handleSubmit }
endDate={endDate}
startDate={startDate}
displayedDate={displayedDate}
range>
<Text>Click me!</Text>
</DateRangePicker>
</View>
)
}
1.not able to select date range.
2. undefined is not an object (evaluating displayedDate.format)
3. Using function component but most of the solutions are available with class component
You can call handleSubmit as follows
onChang={() => handleSubmit()}
And props param is needless in function prototype
const handleSubmit = (props) => {}
Because props is already declared and you don't need to set it as parameter.
If you want to use it as parameter then you should change like this
onChange={() => handleSubmit(props)}
Hope this helps you.
You can change your handleSubmit() function like this
const handleSubmit = (props) => {
if (props.startDate != undefined) {
setStartDate(props.startDate);
}
if (props.displayedDate != undefined) {
setDisplayedDate(props.displayedDate);
}
if (props.endDate != undefined) {
setEndDate(props.endDate);
}
}
Source: issues#15