RTK query function is undefined in react native - react-native

I'm using RTK query to fetch data from the endpoint, setup the folder structure like this
src (root)
features/api/apiSlice.js
screens/ChatScreen.js
I have setup apiSlice.js like this
import {createApi, fetchBaseQuery} from '#reduxjs/toolkit/query/react';
export const apiSlice = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({baseUrl: 'https://api.covid19api.com'}),
endpoints: builder => ({
getEmojiPoints: builder.query({
query: () => '/countries',
}),
}),
});
export const {useGetEmojiPoints} = apiSlice;
then imported the useGetEmojiPoints hook in functional component like this
import {useGetEmojiPoints} from '../features/api/apiSlice';
const {
data: emojiPoints,
isLoading,
isSuccess,
isError,
error,
} = useGetEmojiPoints(); // getting this undefined (not a function)
Setup App.js like this
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
useColorScheme,
Text,
StyleSheet,
} from 'react-native';
import ChartScreen from './src/screens/ChartScreen';
import {ApiProvider} from '#reduxjs/toolkit/query/react';
import {apiSlice} from './src/features/api/apiSlice';
const AppWrapper = () => {
return (
<ApiProvider api={apiSlice}>
<App />
</ApiProvider>
);
};
const App = () => {
const isDarkMode = useColorScheme() === 'dark';
return (
<SafeAreaView style={styles.container}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView>
<ChartScreen />
</ScrollView>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#fff'},
});
export default AppWrapper;
Whats wrong why its giving me undefined for query hook?

It's useGetEmojiPointsQuery, not useGetEmojiPoints.

Related

I want to render a specific static object in react native

I try to render a specific object inside a component (static object) in react native,after i get it with http request from axios API .The (node)server works fine but whenever i try to render it on the screen nothing shows.
Also when i console.log the object its correct(on client side too) but still nothing on simulator screen.
I dont know if i do something wrong or i need hooks for that(im new in react native so excuse me if i open again some same question) .
The code is below :
Client
import React, { Component,
useEffect,
useState } from 'react';
import {
StyleSheet,
Text,
View ,
} from 'react-native';
import axios from 'axios';
var res;
export default function App() {
axios.get('http://x.x.x.x:x/rec')
.then
(function (response){
console.log(response.data);
res = response.data;
})
return (
<View style = {styles.container}>
<Text>This car is a : {res}</Text>
</View>
)};
const styles = StyleSheet.create({
container: {
flex: 1 ,
marginTop:100,
},
});
Server
const app = require('express')();
const { json } = require('body-parser');
const car = {
type: "Fiat",
model : "500"
};
const myCar = JSON.stringify(car);
app.get("/rec",(req,res) => {
res.send(myCar);
console.log("Took Car");
})
app.get("/",(req,res) => {
res.set('Content-Type', 'text/plain');
res.send("You have done it ");
console.log("Took /");
})
var listener = app.listen(8888,()=>{
console.log(listener.address().port);
});
Better to use hook for it
import React, { Component, useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import axios from 'axios';
export default function App() {
const [label, setLabel] = useState('')
axios.get('http://x.x.x.x:x/rec')
.then((response) => {
const data = response.data
const resultLabel = typeof data === 'string' ? data :
`${data.type} ${data.model}`
setLabel(`This car is a : ${resultLabel}`)
})
return (
<View style = {styles.container}>
<Text>{label}</Text>
</View>
)};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop:100,
},
});

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

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

I got this unexpected error in 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

What is the best practice for unit testing my functional component with hooks in React Native?

I've written a simple wrapper component around ScrollView that enables/disables scrolling based on the available height it's given:
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {Keyboard, ScrollView} from 'react-native';
import {deviceHeight} from '../../../platform';
export default function ScrollingForm({
availableHeight = deviceHeight,
children,
}) {
const [scrollEnabled, setScrollEnabled] = useState(true);
const [formHeight, setFormHeight] = useState(0);
const scrollingForm = useRef(null);
const checkScrollViewEnabled = useCallback(() => {
setScrollEnabled(formHeight > availableHeight);
}, [availableHeight, formHeight]);
const onFormLayout = async event => {
await setFormHeight(event.nativeEvent.layout.height);
checkScrollViewEnabled();
};
useEffect(() => {
Keyboard.addListener('keyboardDidHide', checkScrollViewEnabled);
// cleanup function
return () => {
Keyboard.removeListener('keyboardDidHide', checkScrollViewEnabled);
};
}, [checkScrollViewEnabled]);
return (
<ScrollView
ref={scrollingForm}
testID="scrollingForm"
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps="handled"
scrollEnabled={scrollEnabled}
onLayout={onFormLayout}
onKeyboardDidShow={() => setScrollEnabled(true)}>
{children}
</ScrollView>
);
}
I need to write unit tests for this component. So far I have:
import React from 'react';
import {Keyboard} from 'react-native';
import {render} from 'react-native-testing-library';
import {Text} from '../..';
import ScrollingForm from './ScrollingForm';
describe('ScrollingForm', () => {
Keyboard.addListener = jest.fn();
Keyboard.removeListener = jest.fn();
let renderer;
describe('keyboard listener', () => {
it('renders text with default props', () => {
renderer = render(
<ScrollingForm>
<Text>Hello World!</Text>
</ScrollingForm>,
);
expect(Keyboard.addListener).toHaveBeenCalled();
});
it('should call listener.remove on unmount', () => {
renderer = render(
<ScrollingForm>
<Text>Goodbye World!</Text>
</ScrollingForm>,
);
renderer.unmount();
expect(Keyboard.removeListener).toHaveBeenCalled();
});
});
});
I want to also confirm that on layout if the available height is greater than the form height, that scrollEnabled is correctly being set to false. I've tried something like this:
it('sets scrollEnabled to false onFormLayout width 1000 height', () => {
mockHeight = 1000;
const {getByTestId} = render(
<ScrollingForm availableHeight={500}>
<Text>Hello World!</Text>
</ScrollingForm>,
);
const scrollingForm = getByTestId('scrollingForm');
fireEvent(scrollingForm, 'onLayout', {
nativeEvent: {layout: {height: mockHeight}},
});
expect(scrollingForm.props.scrollEnabled).toBe(false);
});