On my Expo I am getting the following error:
I believe this is due to an Async/Await issue in my code, but I am unsure how to fix it. I am new to ReactNative, and am doing the Complete React Course by Stephen, but he does not discuss this issue at all so any help would be much appreciated.
Here is my code:
import { useEffect, useState } from 'react'
import yelp from '/Users/macbook/Coding Stuff React Native/food/src/api/yelp.js'
export default () => {
const [results, setResults] = useState([]);
const [errorMessage, setErrorMessage] = useState('');
const searchApi = async searchTerm => {
console.log('Hi there!');
try {
const response = await yelp.get('/search', {
params: {
limit: 50,
term: searchTerm,
location: 'san jose'
}
});
setResults(response.data.businesses);
} catch (err) {
setErrorMessage('Something went wrong');
}
};
// Call searchApi when component
// is first rendered. BAD CODE!
// searchApi('pasta');
useEffect(() => {
searchApi('pasta');
}, [])
return [searchApi, results, errorMessage];
};
Thank you :)
Update for anyone else stuck on this:
I needed to change
{errorMessage ? <Text>{errorMessage}</Text> : null}
to
{!!errorMessage && <Text>{errorMessage}</Text>}
Related
I am relatively new to javascript and React and I am helping out with a project. I want to create a profile page for a signed in user with information stored in a firebase real time database. But the component is not rendering and the console shows 'Uncaught TypeError: Cannot read properties of null (reading 'username')'. I surmise it is because the data from the database is not being fetched before rendering. The data exists. The profile hook -
import React, { useEffect,useState } from 'react';
import {useAuth} from '../contexts/AuthContext'
import { getDatabase,ref, onValue} from "firebase/database";
function Profile(){
const [userData, setUserData] = useState({});
const currentUser = useAuth();
useEffect(()=>{ putData()
},[])
async function putData(){
let db = getDatabase();
let refd = ref(db,'users/'+ currentUser.currentUser.uid );
onValue(refd, (snapshot) => {
console.log(snapshot.val());
setUserData(snapshot.val());
},
(errorObject) => {
console.log('The read failed: ' + errorObject.name);
})
}
return(
<div>
<h3>Username : {userData.username}</h3>
<h3>Institute name : {userData.institute_name}</h3>
<h3>Accomodation : {userData.accomodation}</h3>
<h3>Phone no. : {userData.phone}</h3>
<h3>Email : {userData.email}</h3>
</div>
);
}
export default Profile;
Does the problem lie with the 'onValue' part or with the react part? Firebase documentation is not helping with my current understanding. Any help on how to accomplish this is appreciated.
useEffect(() => {
try {
//getting previously saved data
// console.log({ SelectedCaseDetails });
const getData = async () => {
const docRef = doc(
db,
"here comes your path to your document"
);
const docSnap = await getDoc(docRef);
console.log("data -->", docSnap.data());
if (docSnap.exists()) {
setData(docSnap.data());
setData(() => ({ ...docSnap.data() }));
}
};
getData();
} catch (error) {
console.log({ error });
}
}, []);
You just have to run your get data function in useEffect that runs when page is loading
Hope this helps 🤗
¯\(ツ)/¯
I'm trying to set the background of my React App to the NASA APOD (Astronomy Picture of the Day). Here is my code for that:
import './App.css';
import React, { useState, useEffect } from "react";
const apiKey = process.env.REACT_APP_NASA_KEY;
function App() {
const [bg, setBg] = useState(null);
async function fetchPhoto() {
const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
const data = await dat.json();
setBg(data);
}
useEffect(() => {
fetchPhoto().then(() => {
document.body.style.backgroundImage = `url('${bg.url}')`
})
}, [])
return null;
}
However, in this code then does not wait for the state to be set and I get the error:
App.js:22 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'url')at App.js:22:1
What am I doing wrong? Thanks!
1. Solution
A simpler way to achieve what you want is by doing like below, this way you don't have to menage asynchronous tasks like you are doing:
import './App.css';
import React, { useState, useEffect } from "react";
const apiKey = process.env.REACT_APP_NASA_KEY;
function App() {
const [bg, setBg] = useState(null);
async function fetchPhoto() {
const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
const data = await dat.json();
setBg(data);
}
useEffect(() => {
fetchPhoto()
}, [])
useEffect(() => {
if(bg){
document.body.style.backgroundImage = `url('${bg.url}')`;
}
}, [bg])
return null;
}
2. Explanation
Your code is not working because just after fetchPhoto() is executed and resolved, React did not yet re-render the components (update the state), therefore bg is still null.
Do you need the state for something else? If not, you could avoid it:
function App() {
useEffect(() => {
const fetchPhoto = async () => {
const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
const data = await dat.json();
if (data) {
document.body.style.backgroundImage = `url('${data.url}')`;
}
}
fetchPhoto();
}, [])
return null;
}
I am trying to test custom hook. I want to know is setState function fire or not.
here is my custom hook
import React from "react";
import axios from "axios";
export default () => {
const [state, setState] = React.useState([]);
const fetchData = async () => {
const res = await axios.get("https://5os4e.csb.app/data.json");
setState(res.data);
};
React.useEffect(() => {
(async () => {
await fetchData();
})();
}, []);
return { state };
};
now I am trying to test this custom hook. I want to know is setState function fire or not .
I tried like this
import moxios from "moxios";
import React from "react";
import { act, renderHook, cleanup } from "#testing-library/react-hooks";
import useTabData from "./useTabData";
describe("use tab data", () => {
beforeEach(() => {
moxios.install();
});
afterEach(() => {
moxios.uninstall();
});
describe("non-error response", () => {
// create mocks for callback arg
const data = [
{
name: "hello"
}
];
let mockSetCurrentGuess = jest.fn();
beforeEach(async () => {
moxios.wait(() => {
const request = moxios.requests.mostRecent();
request.respondWith({
status: 200,
response: data
});
});
});
test("calls setState with data", async () => {
React.useState = jest.fn(() => ["", mockSetCurrentGuess]);
const { result, waitForNextUpdate } = renderHook(() => useTabData());
console.log(result);
//expect(mockSetCurrentGuess).toHaveBeenCalledWith(data);
});
});
});
You should not mock the React internals. This is incorrect. Either ways, this code has no effect in mocking due to closures. Even if it worked, no point in testing if you are mocking the real implementation, isn't it ? :)
I would recommend to try to get grasp of what react hook is doing in your code.
You have a state in your custom hook:
const [state, setState] = React.useState([]);
.
.
return [state]; //you are returning the state as ARRAY
#testing-library/react-hooks allows you to debug and get value of current outcome of hook.
const { result, waitForNextUpdate } = renderHook(() => useTabData());
const [foo] = result.current; // the array you returned in hook
expect(foo).toEqual('bar'); //example assertion
I would stop here and allow you to learn and debug.
I am currently working on a chat application and for some reason every time I pass in my array of messages as a prop to another component it passes in a number to the component instead of the message object. I have tried a lot of different methods of passing it in regarding using multiple components etc but it seems to still be passing in the number of elements for some reason. Any help is appreciated... code is below
Component receiving the props
import React, { useEffect } from 'react'
import Message from '../../Message/Message'
function Messages({ messages }) {
useEffect(() => {
console.log(messages)
}, [messages])
return (
<div>
test
</div>
)
}
export default Messages
// Import React dependencies.
import React, { useEffect, useState, } from "react";
// Import React dependencies.
import io from 'socket.io-client'
import axios from 'axios'
import Messages from './Messages/Messages'
import uuid from 'react-uuid'
import { Redirect } from 'react-router-dom'
// Import the Slate components and React plugin.
const ENDPOINT = 'http://localhost:5000/'
export const socket = io.connect(ENDPOINT)
const LiveChatFunction = ({ group_id }) => {
// Add the initial value when setting up our state.
const [message, setValue] = useState("")
const [user, setUser] = useState("")
const [groupId, setGroup] = useState('')
const [messages, setMessages] = useState([])
const [toLogin, userAuth] = useState(false)
useEffect(() => {
setGroup(group_id)
axios.post('http://localhost:5000/api/users/refresh_token', null, { withCredentials: true }).then(data => {
if (!data.data.accessToken) {
userAuth(true)
}
})
axios.get('http://localhost:5000/api/users/userInfo', { withCredentials: true }).then(data => {
setUser(data.data.user)
})
socket.on(`message-${group_id}`, data => {
setMessages(messages.push(data))
});
axios.get(`http://localhost:5000/live/${group_id}`).then(x => {
console.log(x.data)
})
}, [group_id, messages])
function setClick() {
const data = {
messageId: uuid(),
user,
groupId,
message
}
socket.emit('message', data)
}
if (toLogin) {
return (
<Redirect to="/login" />
)
}
return (
<div>
<input placeholder="message" type="text" onChange={value => {
setValue(value.target.value)
socket.emit('typing-message', { username: user, time: new Date() })
}} />
<button onClick={setClick}>Submit</button>
<Messages messages={messages} />
</div>
)
}
export default LiveChatFunction;
I have added some comments of what I think you can change:
useEffect(() => {
const recieveFunction = (data) => {
//using callback so no dependency on messages
setMessages((messages) => messages.push(data));
};
async function init() {
//next line is pointless, this runs when group_id
// has changed so something must have set it
// setGroup(group_id);
await axios //not sure if this should be done before listening to socket
.post(
'http://localhost:5000/api/users/refresh_token',
null,
{ withCredentials: true }
)
.then((data) => {
if (!data.data.accessToken) {
userAuth(true);
}
});
await axios
.get('http://localhost:5000/api/users/userInfo', {
withCredentials: true,
})
.then((data) => {
setUser(data.data.user);
});
//start listening to socket after user info is set
socket.on(`message-${group_id}`, recieveFunction);
axios
.get(`http://localhost:5000/live/${group_id}`)
.then((x) => {
console.log(x.data);
});
}
init();
//returning cleanup function, guessing socket.off exists
return () =>
socket.off(`message-${group_id}`, recieveFunction);
}, [group_id]); //no messages dependencies
console.log('messages are now:',messages);
If messages is still not set correctly then can you log it
So I think I found your problem:
In your useEffect hook, you're setting messages to the wrong thing.
socket.on(`message-${group_id}`, data => {
setMessages(messages.push(data))
});
An example:
const m = [].push();
console.log(m);
// m === 0
const n = [].push({});
console.log(n);
// n === 1
As you can see this is the index.
So what you need is:
socket.on(`message-${group_id}`, data => {
messages.push(data);
setMessages(messages);
});
This will set messages to the array of messages.
I'm a newbie in React Native and struggling to make a Facebook login for my app.
I finished configuring all the requirements for my app, Firebase and Facebook for developers.
The thing is when I pressed login, at the first time, it kept repeating again the Login permission. Until the data receive the accessToken and id, my app might/might not navigate to the Main screen (login succeed). But just 1 second later, the screen prompted and showed the Login Manager again. And it keeps repeating that. I really don't know why.
Is it something wrong with my code?. I'm thinking it kept repeating because It must do that until it gets the data need ( promise resolved)
Here's the code:
import React, { useEffect, useState } from 'react';
import {
View,
ImageBackground,
StyleSheet,
Alert
} from 'react-native';
import {
Layout
} from '#ui-kitten/components';
import { GoogleSignin, GoogleSigninButton, statusCodes } from '#react-native-community/google-signin';
import { firebase } from '#react-native-firebase/auth';
import { LoginButton, LoginManager, AccessToken } from 'react-native-fbsdk';
const GoogleLogIn = (props) => {
const [userInfo, setUserInfo] = useState(null);
const [isLogIn, setIsLogIn] = useState(false);
// Facebook log in
const _facebookLogin = async () => {
try{
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
console.log(result);
if(result.isCancelled){
console.log('Login is cancelled');
}else if(result.grantedPermissions){
console.log('DONE')
const data = await AccessToken.getCurrentAccessToken();
console.log(data);
const cred = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
const firebaseCred = await firebase.auth().signInWithCredential(cred);
setIsLogIn(true);
setUserInfo(data.userID);
props.navigation.navigate('AppNavigator', {screen: 'Welcome'})
}
}catch(err){
console.log(err);
throw err;
}
}
return(
<View style={styles.background}>
<LoginButton
onLoginFinished={_facebookLogin}
onLogoutFinished={() => console.log('Logout!')}
/>
</View>
);
};
const styles = StyleSheet.create({
background: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export default GoogleLogIn;
Here's the weird behavior:
Error_repeating asking permissions for login Facebook
PLEASE HELP!
The useState function seems to cause rendering again and cause problems.
You can try use useCallback
useCallback will return a memoized version of the callback that
only changes if one of the dependencies has changed. This is useful
when passing callbacks to optimized child components that rely on
reference equality to prevent unnecessary renders (e.g.
shouldComponentUpdate).
Usage
const _facebookLogin = useCallback( async () => {
try{
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
console.log(result);
if(result.isCancelled){
console.log('Login is cancelled');
}else if(result.grantedPermissions){
console.log('DONE')
const data = await AccessToken.getCurrentAccessToken();
console.log(data);
const cred = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
const firebaseCred = await firebase.auth().signInWithCredential(cred);
setIsLogIn(true);
setUserInfo(data.userID);
props.navigation.navigate('AppNavigator', {screen: 'Welcome'})
}
}catch(err){
console.log(err);
throw err;
}
},[])
Here's the code I found it worked well. You can put this in a button for customizing
const facebookLogin = async () => {
try {
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']).then(
(res) => {
if(res.isCancelled){
console.log('Something went wrong, please try again');
}else{
AccessToken.getCurrentAccessToken().then(
async (data) => {
console.log(data.accessToken.toString())
const cred = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
const firebaseCred = await firebase.auth().signInWithCredential(cred);
setIsLogIn(true);
setUserInfo(data.userID);
props.navigation.navigate('AppNavigator', {screen: 'Welcome'})
}
)
}
}
)
} catch (err) {
console.log(err);
}
}