I have the following React Native module:
import React, {useContext, useEffect} from 'react';
import {Image, StyleSheet} from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import AuthContext from '../context/auth/authContext';
const Intro = () => {
const getLocalUser = async () => {
try {
const value = await AsyncStorage.getItem('user');
return value;
} catch (error) {
console.log(error);
}
};
const localUser = getLocalUser();
const {setUser} = useContext(AuthContext);
useEffect(() => {
setUser({localUser});
}, []);
return (
<Image
style={styles.image}
source={require('../static/logo_intro.png')}
/>
);
}
const styles = StyleSheet.create({
image: {
width: 430,
resizeMode: 'center',
paddingTop: 500
}
})
export default Intro;
That raises the following error: [Tue Sep 08 2020 15:00:47.220] ERROR TypeError: create is not a function. (In 'create()', 'create' is undefined).
I checked this question, but I am already doing it.
It might be that you have to use useState instead of useEffect.
See https://github.com/facebook/react/issues/15194 for a lead into why this error is shown this way.
Related
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,
},
});
I know the title is very vague but I hope someone may have an idea.
I want to perform a simple snapshot test on one of my screens with jest but I keep getting errors like this:
Warning: React.jsx: 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.
14 | test('renders correctly', async () => {
15 | const tree = renderer.create(
> 16 | <AuthContext.AuthProvider>
| ^
17 | <AuthContext.Consumer>
18 | <ValidateScreenPhrase ref={(navigator)=>{ setNavigator(navigator) }}/>
19 | </AuthContext.Consumer>
The problem is probably that I use a Context build that looks as follows:
import React, { useReducer } from 'react'
export default (reducer, actions, defaultValue) => {
const Context = React.createContext();
const Provider = ({children}) => {
const [state, dispatch] = useReducer(reducer, defaultValue)
const boundActions = {}
for (let key in actions){
boundActions[key] = actions[key](dispatch);
}
return (
<Context.Provider value={{ state, ...boundActions }}>{children}</Context.Provider>
)
}
return { Context, Provider }
}
from here I then build different Contexts that contain functions and states as e.g.:
import createDataContext from "./createDataContext"
import { navigate } from '../navigationRef'
const authReducer = (state, action) => {
switch (action.type){
case 'clear_error_message':
return { ...state, errorMessage: '' }
default:
return state
}
}
const validateInput = (dispatch) => {
return (userInput, expected) => {
if (userInput === expected) {
navigate('done')
}
else{dispatch({ type: 'error_message', payload: 'your seed phrase was not typed correctly'})}
}
}
export const { Provider, Context } = createDataContext(
authReducer,
{ clearErrorMessage },
{ errorMessage: '' }
)
Now the screen that I want to test is this:
import React, { useState, useContext, useEffect } from 'react'
import { StyleSheet, View, TextInput, SafeAreaView } from 'react-native'
import { Text, Button } from 'react-native-elements'
import { NavigationEvents } from 'react-navigation'
import { Context as AuthContext } from '../context/AuthContext'
import BackButton from '../components/BackButton'
const ValidateSeedPhraseScreen = ({navigation}) => {
const { validateInput, clearErrorMessage, state } = useContext(AuthContext)
const [seedPhrase, setSeedPhrase] = useState('')
const testPhrase = 'blouse'
const checkSeedPhrase = () => {
validateInput(seedPhrase, testPhrase)}
return (
<SafeAreaView style={styles.container}>
<NavigationEvents
onWillFocus={clearErrorMessage}
/>
<NavigationEvents />
<BackButton routeName='walletInformation'/>
<View style={styles.seedPhraseContainer}>
<Text h3>Validate Your Seed Phrase</Text>
<TextInput
style={styles.input}
editable
multiline
onChangeText={(text) => setSeedPhrase(text)}
value={seedPhrase}
placeholder="Your Validation Seed Phrase"
autoCorrect={false}
autoCapitalize='none'
maxLength={200}
/>
<Button
title="Validate"
onPress={() => checkSeedPhrase(seedPhrase, testPhrase)}
style={styles.validateButton}
/>
{state.errorMessage ? (<Text style={styles.errorMessage}>{state.errorMessage}</Text> ) : null}
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginLeft: 25,
marginRight: 25
},
seedPhraseContainer:{
marginTop: '40%'
},
input: {
height: 200,
margin: 12,
borderWidth: 1,
padding: 10,
fontSize: 20,
borderRadius: 10
},
validateButton:{
paddingBottom: 15
}
})
export default ValidateSeedPhraseScreen
Here I import the AuthContext and make use of the function validateInput and state from the Context. Here I also don't know how to bring these into the testing file
and my test so far looks like this:
import React, {useContext} from "react";
import renderer from 'react-test-renderer';
import { setNavigator } from '../../src/navigationRef';
import ValidateScreenPhrase from '../../src/screens/ValidateSeedPhraseScreen'
import { Provider as AuthProvider, Context as AuthContext } from '../../src/context/AuthContext';
jest.mock('react-navigation', () => ({
withNavigation: ValidateScreenPhrase => props => (
<ValidateScreenPhrase navigation={{ navigate: jest.fn() }} {...props} />
), NavigationEvents: 'mockNavigationEvents'
}));
test('renders correctly', async () => {
const tree = renderer.create(
<AuthProvider>
<AuthContext.Consumer>
<ValidateScreenPhrase ref={(navigator)=>{ setNavigator(navigator) }}/>
</AuthContext.Consumer>
</AuthProvider>, {}).toJSON();
expect(tree).toMatchSnapshot();
});
I already tried out all lot of changes with the context and provider structure. I then always get errors like: "Authcontext is undefined" or "render is not a function".
Does anyone have an idea about how to approach this?
Making an uber clone with React Native, while setting up the Redux while making the app in the first place, the Metro bundler returned these errors:
Error: The slice reducer for key "nav" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined. If you don't want to set a value for this reducer, you can use null instead of undefined.
at node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in registerError
......
Invariant Violation: "main" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.
.....
This is the App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';
import { store } from './store';
// 1. Set up redux - done
//
export default function App() {
return (
<Provider store={store}>
<View style={styles.container}>
<Text>Uber</Text>
</View>
</Provider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
This is the navSlice.js
import { createSlice } from "#reduxjs/toolkit";
const innitialState = {
origin: null,
destination: null,
travelTimeInformation: null,
}
export const navSlice = createSlice({
name:"nav",
innitialState,
reducer: {
setOrigin: (state, action) =>{
state.origin = action.payload;
},
setDestination: (state, action) =>{
state.destination = action.payload;
},
setTravelTimeInformation: (state, action) =>{
state.travelTimeInformation = action.payload;
},
}
});
export const { setOrigin, setDestination, setTravelTimeInformation } = navSlice.actions;
// Selectors
export const selectOrigin = (state) => state.nav.origin;
export const selectDestination = (state) => state.nav.destination;
export const selectTravelTimeInformation = (state) => state.nav.travelTimeInformation;
export default navReducer = navSlice.reducer;
This is the Store.js
import { configureStore } from "#reduxjs/toolkit";
import navReducer from "./slices/navSlice";
export const store = configureStore({
reducer: {
nav: navReducer,
},
});
What are the reasons I am getting back these errors?
How can I fix this?
Is there an issue with how I am setting up the redux?
Change the spelling in navslice to reducers and restart emulator
Your innitialState spelling is wrong.
Change it to initialState
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>
</>
);
}
I have been for several hours trying to get an API to be called in ReactNative useEffect hook. Sometimes when I restart my app the value is resolved. But most of the time, I have an Unhandled promise rejection. I googled and tried various methods. I tried using .then etc.. I just can't figure it out.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
async function setToState() {
const val = await getBlog();
setPost(val);
}
setToState();
},[]);
return (
<View>
<Text>Here { console.log(post) }</Text>
</View>
);
};
ShowScreen.navigationOptions = ({ navigation }) => {
return {
headerRight: (
<TouchableOpacity
onPress={() =>
navigation.navigate('Edit', { id: navigation.getParam('id')
})}
>
<EvilIcons name="pencil" size={35} />
</TouchableOpacity>
)
};
};
const styles = StyleSheet.create({});
export default ShowScreen;
What you could do is something like this:
....
....
const [post, setPost] = useState([]);
const [isMounted, setIsMounted] = useState(false);
const getBlog = async () => {
const result = await jsonServer.get(`http://0.0.0.0/blog/docroot/jsonapi/node/article/${id}`);
return result;
}
useEffect(() => {
setIsMounted(true)
async function setToState() {
// using try catch I'm handling any type of rejection from promises. All errors will move to catch block.
try{
const val = await getBlog();
// checking if component is still mounted. If mounted then setting a value. We shouldn't update state on an unmounted component.
if(isMounted){
setPost(val);
}
} catch(err){
console.log("Error", err)
}
}
setToState();
return () => {
// Setting is mounted to false as the component is unmounted.
setIsMounted(false)
}
},[]);
I believe this will solve your Unhandled promise rejection error. Please try if it still doesn't solve the issue will create the same in Sanck.
I think my issue was not just promise, the issue is also seems to be me not handling undefined/null in the state. The below code is working for me.
import React, { useState, useContext, useEffect } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, FlatList } from 'react-native';
import { EvilIcons } from '#expo/vector-icons';
import jsonServer from '../api/jsonServer';
const ShowScreen = ({ navigation }) => {
const id = navigation.getParam('id');
const [post, setPost] = useState([]);
const getBlog = async () => {
const result = await jsonServer.get(`http://hello.com/jsonapi/node/article/${id}`).then(
res => {
setPost(res)
return res;
}, err => {
console.log(err);
});
}
useEffect(() => {
setPost(getBlog());
},[]);
return (
<View>
<Text>{ post.data ? post.data.data.id : "" }</Text>
</View>
);
};
export default ShowScreen;
Note: I am setting the state in useEffect as well as in the request. I am yet to check if I can just do it once.