Function passed as argument to custom React Hook is causing problems - javascript

The Need:
I am using RedwoodJS for making a Fullstack app. It provides us with hook useAuth() which gives a state isAuthenticated telling if user is logged in or not.
I want to make some Queries on Web side whenever user Logs in. Not whenever isAuthenticated from useAuth() is changed. (example: on page loads, isAuthenticated is set to false from undefined.. but this doesn't mean user logged out. What if I want to run certain function only on log out?
Tried Solution:
I wrote this custom hook:
export type CurrentUser = ReturnType<typeof useAuth>['currentUser']
interface HookProps {
// similarly for onLogout
onLogin?: (currentUser: CurrentUser) => void
}
export function useAuthenti(props: HookProps): CurrentUser | false {
const { onLogin } = props
const { isAuthenticated, currentUser } = useAuth()
const wasAuthenticated = usePrevious(isAuthenticated);
const [currentUserOrFalse, setCurrentUserOrFalse] = useState<CurrentUser | false>(false)
useEffect(() => {
console.log(`isAuthenticated CHANGED: ${wasAuthenticated} => ${isAuthenticated}`)
if (isAuthenticated) {
setCurrentUserOrFalse(currentUser)
if (wasAuthenticated === undefined) {
console.log(`false login 1`)
} else if (wasAuthenticated === false) {
console.log(`real login [MAKE API CALLS]`)
if (onLogin) {
console.log(`1. [inside] calling onlogin`)
onLogin?.(currentUser)
console.log(`4. [inside] called onLogin`)
}
} else if (wasAuthenticated === true) {
console.log(`false login 2`)
} else {
console.log(`false login 3`)
}
} else {
setCurrentUserOrFalse(false)
if (wasAuthenticated === undefined) {
console.log(`false logout 1`)
} else if (wasAuthenticated === false) {
console.log(`false logout 2`)
} else if (wasAuthenticated === true) {
console.log(`real logout [MAKE API CALLS]`)
} else {
console.log(`false logout 3`)
}
}
}, [isAuthenticated])
return currentUserOrFalse
}
and I am using this hook as follows:
export function Initialize({ children }: ComponentProps) {
const [getMyData, { loading: loading_MyData, data: data_MyData }] = useLazyQuery(MY_DATA_QUERY)
const [getAllPosts, { loading: loading_AllPosts, data: data_AllPosts }] = useLazyQuery(ALL_POSTS_QUERY)
useAuthenti({
onLogin: (currentUser: CurrentUser) => {
console.log(`2. [outside] onLogin start`)
getMyData()
getAllPosts()
console.log(`3. [outside] onLogin done`)
},
})
useEffect(() => {
if (data_MyData && data_AllPosts) {
console.log(data_MyData)
console.log(data_AllPosts)
}
}, [data_MyData, data_AllPosts])
return (
<>
{children}
</>
)
}
The Problem:
In the above usage, as you can see i am providing onLogin function prop to the useAuthenti custom hook. Because i want that function to run ON LOGIN and make the api calls in it.
However, the code isn't working as expected every time. sometimes it is working, other times it's not. (also weird.. seems like a race condition?)
When it's not working, I don't see any console logs (hinting the onLogin was never called), BUT, in networks tab I DO see calls being made by createHttpLink.js:100. it's failing also. During this case (when it doesn't work as expected), i see user login call succeeding BUT redwood js' getCurrentUser call isn't made. (you can ignore the redwoodjs part if you're unfamiliar with redwood js, just focus on the hook part) How are the apis that are inside onLogin running without any console logs surrounding it?
Networks Tab Debug
The red box shows attempt one at login (doesn't work)
The green box shows attempt two at login (works)
Additional Note:
Apparently, only passing the functions as arguments is causing them to run EVEN if I don't run them inside the hook. Why are the functions passed as arguments to hook running on their own?
Is the way I am making a custom React Hook wrong? What am I doing wrong here if anyone could please let me know.
Is there a race condition as sometimes it'w working and sometimes it's not?
How do I proceed?

I don't understand why, but writing a custom hook to replace useLazyQuery that returns a Promise fixed all the problems.
This Github thread helped: https://github.com/apollographql/react-apollo/issues/3499
Particularly this answer: https://github.com/apollographql/react-apollo/issues/3499#issuecomment-539346982
To which I extended to support Typescript and States like loading error and data. (his version is useLazyQuery, extended hook is useLazyQueryModded)
useLazyQueryModded.tsx
import { useState } from 'react';
import { DocumentNode, ApolloError, useApolloClient, OperationVariables } from '#apollo/client';
type QueryVariables = Record<string, any>;
type UseLazyQueryResult<TData, TVariables extends QueryVariables> = [
(variables?: TVariables) => void,
{ data: TData | undefined, error: ApolloError | undefined, loading: boolean }
];
function useLazyQuery<TData = any, TVariables = OperationVariables>(query: DocumentNode) {
const client = useApolloClient()
return React.useCallback(
(variables: TVariables) =>
client.query<TData, TVariables>({
query: query,
variables: variables,
fetchPolicy: 'cache-first'
}),
[client]
)
}
function useLazyQueryModded<TData = any, TVariables extends QueryVariables = QueryVariables>(
query: DocumentNode
): UseLazyQueryResult<TData, TVariables> {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<TData | undefined>(undefined);
const [error, setError] = useState<ApolloError | undefined>(undefined);
const runQuery = useLazyQuery<TData, TVariables>(query);
const executeQuery = async (variables?: TVariables) => {
setLoading(true);
setData(undefined);
setError(undefined);
try {
const result = await runQuery(variables);
setData(result.data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
const result = { data, error, loading };
return [executeQuery, result];
}
export { useLazyQueryModded };
If anyone can explain why useLazyQuery wasn't returning a Promise or why even when it didn't code worked sometime, I'd be relieved of this itch.

Related

state is not getting updated for a new user in react native

My app has a test sheet, if a user passes the test he is shown a passing screen and then the state is saved using asyncstorage. But the problem here is, let's say i have user A and user B and user A is currently logged in, he passed the test and the app shows him passing screen and the state is saved. Now user A logs out and user B logs in, he is a completely new user he has never given test before but my app has still saved the state for the user A and keeps showing passing screen even to the user B rather it should not.Can someone help me with this issue?
code:
import React ,{useState, useEffect} from "react";
import {View, Alert, Image, StyleSheet, Text, Modal, TouchableOpacity, TouchableHighlight} from 'react-native';
import Voice from 'react-native-voice';
import auth from '#react-native-firebase/auth';
import AsyncStorage from '#react-native-async-storage/async-storage';
const key = auth().currentUser.uid + "hasPassed"
export const hasPassed = async () => {
return AsyncStorage.getItem(key).then(result => result != null ? JSON.parse(result) : undefined).catch(e => console.log(e))
}
export const setHasPassed = async (newPassed) => {
return AsyncStorage.setItem(key, JSON.stringify({hasPassed: newPassed})).catch(e => console.log(e))
}
export default alpht =({navigation}) => {
function Check() {
if (results.includes(words[index])){
Alert.alert('Correct!','You are learning so well!');
if(index==7) {
if(count<=5)
{
setHasPassed(true).then(() => setshowpass(true))
// setshowpass(true);
}
else{
console.log(count)
Alert.alert('fail','fail');
}
}
if (index==7){
setndis(true);
setdis(true);
setidis(true);
}
else{
setndis(false);
setdis(true);
setidis(true);
}
}
else{
Alert.alert('Ops!','Looks like you went wrong somewhere. Try again!');
setcount(count+1);
setdis(true);
setndis(true);
if(count==5){
Alert.alert('Restest', 'Looks like you had way too many mistakes!')
setind(0);
setcount(0);
setdis(true);
}
}
}
const words=['ceket', 'çilek', 'elma', 'fare', 'öğretmen', 'otobüs', 'şemsiye', 'uçak'];
const [show, setshow]=useState('');
const [showpass, setshowpass]=useState(false);
useEffect(() => {
//console.log(auth().currentUser.uid);
setshow(true);
}, []);
useEffect(() => {
const getState = async () => {
const result = await hasPassed()
setshowpass(result ? result.hasPassed : false)
}
getState()
}, []);
console.log(auth().currentUser.uid)
if (showpass === false) {
// setshow(true)
console.log('hey');
return null
}
return (
//... other code
)
}
my user logs out using auth().signOut() by the way!
It would be great if this issue gets solved i am dealing with it for the past 4,5 days now!
I think this is the problem:
const key = auth().currentUser.uid + "hasPassed"
export const hasPassed = async () => {
return AsyncStorage.getItem(key).then(result => result != null ? JSON.parse(result) : undefined).catch(e => console.log(e))
}
export const setHasPassed = async (newPassed) => {
return AsyncStorage.setItem(key, JSON.stringify({hasPassed: newPassed})).catch(e => console.log(e))
}
key is defined at the top level, outside of the react lifecycle, and thus is subject to having stale values. auth().currentUser may change, the value of key will not (I think). Instead of storing key as a string, try storing it as a function:
// every time getKey is called it will get a fresh instance of currentUser
const getKey = ()=>auth().currentUser.uid + "hasPassed"
export const hasPassed = async () => {
return AsyncStorage.getItem(getKey()).
then(result => result != null ? JSON.parse(result) : undefined).
catch(e => console.log(e))
}
export const setHasPassed = async (newPassed) => {
return AsyncStorage.setItem(
getKey(),
JSON.stringify({hasPassed: newPassed})
).catch(e => console.log(e))
}
I don't know exactly what's going wrong in your code, but I believe that the piece of code in your useEffect is fetching the state of user A no matter who is logged in ( state persistence). try testing with user C. check out firebase state persistence in their official documentation. I hope I gave you some hints to solve this issue.

Throttling navigation to prevent the browser from hanging - React

Throttling navigation to prevent the browser from hanging. See https://crbug.com/882238. Command line switch --disable-ipc-flooding-protection can be used to bypass the protection
This is the full warning it is throwing:
My code:
import { login, resetState } from "../../store/auth/authActions";
import { isAuthenticated } from "../../utils/auth";
const Login = ({ history }) => {
const dispatch = useDispatch();
//joi schema
....
//react hook form setup
const {
register,
handleSubmit,
setError,
clearErrors,
formState: { errors },
} = useForm({
resolver: joiResolver(schema),
});
useEffect(() => {
dispatch(resetState(clearErrors));
if (isAuthenticated) {
//AS SOON AS I UNCOMMENT THE NEXT LINE THROWS THE WARNING IN LOOP
// history.push("/dashboard");
}
}, [clearErrors, dispatch, history]);
const onSubmit = (data) => {
dispatch(resetState(clearErrors));
dispatch(login(data));
};
// global state
const serverSideErrors = useSelector((state) => state.auth.errors);
useEffect(() => {
Object.entries(serverSideErrors).forEach(([i, v]) => {
setError(i, {
message: v[0],
});
});
}, [serverSideErrors, setError]);
return (...)
export default Login;
here, you can see the line :history.push("/dashboard"); this is what seems to be causing the issue. If I comment this line. That warning will be gone.
I have also tried with:
useEffect(() => {
dispatch(resetState(clearErrors));
if (isAuthenticated) {
history.push("/dashboard");
}
}, []);
but same issue. How, could I fix this? What I am missing here?
My guess is that dispatch(resetState(clearErrors)); changes clearErrors references. Since clearErrors is a dependency of the same hook and it changes in the hook, it gets called in loop, which runs history.push("/dashboard"); repeatedly.
You should be doing dispatch(resetState(clearErrors)); conditionally otherwise it'll always end up in loop.
PS: If you could post a minimal repo of this issue, I might be able to help better.

Problems with react-native-fbsdk: Login not working

I've been out of work for a while, and a friend who owns a startup company tasked me with fixing the Facebook Login functionality of his app. Big opportunity for me.
None of the devs who built/worked on it are around anymore, and the app's code is written in such a confusing way. So I'm bringing it here in hopes that I can get help to resolve it.
The Issue
When you open the app there's an intro screen, you close that, and then it prompts you to login with Facebook.
When Login is clicked, it loads for a bit, and then just returns back to the initial intro screen.
The debugger console displays:
[CodePush] Reporting binary update (2.0.0)
[CodePush] Report status failed: {"appVersion":"2.0.0"}
Not really familiar with CodePush, and I've been investigating that on and off, unsure if that's even related to my issue or not. I went through their docs and made sure this project was configured right anyway.
Then I did the same with Facebook SDK. I checked the docs and compared the instructions to what was done in the app, to see if the previous devs configured it properly. Everything looks right.
Docs I'm referring to:
https://developers.facebook.com/docs/facebook-login/android/v2.2
NOTE I can't do steps 5 and beyond, because you need to log in to complete those, and my FB account is not connected to the app. Currently waiting to hear back from someone who worked on this. Likely not relevant anyway, as everything else was done correctly.
I found the code for the Login Button in the codebase, but there's a lot of files importing to each other, and I'm not sure what exactly does what. I will post the bread trail I followed below.
Here I track the Login button to find the source of the implemented feature.
I skip files that are unnecessary, such as the screen that displays the greeting along with the Login button. I followed the import statements until I reached the following:
LoginButtonContainer.js
// #flow
import { connect } from 'react-redux'
import { loginWithFbButtonPressed } from '../redux/actions'
import LoginWithFbButton, {
Props as LoginButtonComponentProps,
} from '../components/LoginWithFbButton' // <<<~~~ This file confused me, so next I checked here
type LoginButtonProps = {
onPress: any => void,
}
const mapDispatchToProps = (dispatch: any) => ({
login: () => dispatch(loginWithFbButtonPressed()),
})
const mergeProps = (
_,
dispatchProps: { login: () => void },
ownProps: LoginButtonProps
): LoginButtonComponentProps => {
const { login } = dispatchProps
const { onPress = () => null } = ownProps
return {
onPress: () => {
onPress()
login()
},
}
}
export default connect(null, mapDispatchToProps, mergeProps)(LoginWithFbButton)
LoginWithFbButton.js
// #flow
import * as React from 'react'
import { View, Text, Dimensions, StyleSheet } from 'react-native'
import Icon from 'react-native-vector-icons/FontAwesome'
import Entypo from 'react-native-vector-icons/Entypo'
import { heavyGray, lightGray, facebookColor } from '../constants/colors'
export type Props = {
onPress: () => void,
advisoryText: string,
}
/**
* #class LoginWithFbButton
* Facebook compliant component to login with FB, do not use any other medium to login with FB as to not break the TOS
*/
class LoginWithFbButton extends React.PureComponent<Props> {
render() {
const {
onPress,
advisoryText = `We don't post anything to Facebook`,
} = this.props
return (
<View style={styles.container}>
<Icon.Button
name="facebook"
backgroundColor={facebookColor}
onPress={onPress}
style={styles.facebookButton}
>
Login with Facebook
</Icon.Button>
<View style={styles.advisoryContainer}>
<Entypo
color={heavyGray}
name="info-with-circle"
style={styles.infoIcon}
/>
<Text style={styles.advisoryText}>{advisoryText}</Text>
</View>
</View>
)
}
}
export default LoginWithFbButton
This led me to search for <LoginWithFBButton> but I do not see that anywhere in the app.
Despite me not finding that tag anywhere, the Login button is displayed on the screen.
Lastly(maybe) there's a file called facebook.js
facebook.js
// #flow
import FBSDK from 'react-native-fbsdk';
const { LoginManager, AccessToken } = FBSDK;
export type OpenFBLoginResult =
| {
fbData: string,
}
| {
error: any,
};
export type FacebookDataState = {
accessToken: string,
applicationID: string,
declinedPermissions: Array<string>,
expirationTime: number,
lastRefreshTime: number,
permissions: Array<string>,
userID: string,
};
const getToken = (): Promise<?string> =>
AccessToken.getCurrentAccessToken().then(t => (t ? t.accessToken : null));
const openFBLogin = (): Promise<OpenFBLoginResult> =>
LoginManager.logInWithReadPermissions([
'public_profile',
'email',
]).then(result => {
if (result.isCancelled) {
return {
error: 'User cancelled Facebook login',
};
}
return AccessToken.getCurrentAccessToken().then(fbData => ({
...result,
fbData,
}));
});
const logOut = (): Promise<void> => LoginManager.logOut();
/**
* Dummies
*/
class GraphRequest {}
const GraphRequestManager = () => ({
addRequest: () => ({
start: () => null,
}),
});
const fetchUserImage = (facebookId: string) => {
return new Promise((resolve, reject) => {
const path = `/${facebookId}/picture`;
// Create a graph request asking for user information with a callback to handle the response.
const infoRequest = new GraphRequest(
path,
null,
(error: ?Object, result: ?Object) => {
if (error) {
reject(error);
} else {
resolve(result);
}
}
);
// Start the graph request.
new GraphRequestManager().addRequest(infoRequest).start();
});
};
export default {
openFBLogin,
getToken,
logOut,
fetchUserImage,
};
// export function* openFBLogin(): OpenFBLoginResult {
// type openFBLoginResultType = { +type: string, +token: string }
// const response: openFBLoginResultType = yield call(Expo.Facebook.logInWithReadPermissionsAsync, FBAPP_ID, {
// permissions: FB_PERMISSIONS
// })
// let error: ?any = null
// switch (response.type) {
// case 'success':
// const { token } = response
// yield call(_setFBToken, token)
// yield put(actions.loginFb.success('', token))
// return { token }
// case 'cancel':
// error = CANCELLED_ERROR
// yield put(actions.loginFb.failure('', error))
// return { error }
// default:
// error = JSON.stringify(response)
// yield put(actions.loginFb.failure('', error))
// return { error }
// }
// }
This is probably not a great post, but a lot rides on this, and I'm hoping something obvious sticks out to someone more experienced than I.
If I've left anything out, I will update this post with whatever I can. Thanks all.

React doesn't re-render after State change on button click in ReactJS (preact)

I have the following code which is working except that it doesn't re-render the page after pressing a button.
I have two different buttons being displayed depending on wether a user is part of a room or not. If a user is part of a room they can click on LEAVE which will execute an API call. I would then like the component to reload and for the button to display JOIN (since they are no longer part of that room).
import JoinedRooms from '../../components/matrix_joined_rooms';
import JoinRoom from '../../components/matrix_function_join_room';
import LeaveRoom from '../../components/matrix_function_leave_room';
import { useEffect, useState } from 'preact/hooks';
const JoinLeaveButton = ({ name, roomId }) => {
const joinedRooms = JoinedRooms();
const [x, setX] = useState(5);
useEffect(() => console.log("re-render because x changed:", x), [x])
const handleXClick = (number) => {
setX(number)
}
if (joinedRooms.includes(name)) {
return <button name={roomId} onClick={() => {
LeaveRoom(roomId);
handleXClick(10);
}
}>LEAVE</button>
} else {
return <button name={roomId} onClick={() => {
JoinRoom(roomId);
handleXClick(20);
}
}>JOIN</button>
}
}
export default JoinLeaveButton;
my JoinRoom and LeaveRoom components are a simple API call which look like this:
const JoinRoom = (roomId) => {
fetch(`https://example.com/_matrix/client/r0/rooms/${roomId}/join`, {
method: "POST",
headers: {
Authorization: `Bearer ${localStorage.getItem("mx_access_token")}`
}
});
}
export default JoinRoom;
The funcionality of the button itself works, the problem is that I have to manually reload the page for it to be displayed correctly.
I have put a dummy state in place which is executed whenever you press the button and it also logs to the console properly.
I was under the impression, that changing a state should re-render a component in React (or in this case preact).
Thanks!
In essense: you need to store the state of the joined rooms somewhere and have that state updated everytime a user joins or leaves a room.
I've gone way overboard here, but a custom hook like this makes a lot of sense:
// api calls
const fetchRooms = async userid => { ... }
const joinRoom = async (userId,roomId) => { ... }
const leaveRoom = async (userId,roomId) => { ... }
// custom hook
const useRooms = (userId) => {
const [fetching,setFetching] = useState(true);
const [error,setError] = useState(false);
// joinedRooms state is an empty array when this hook is first used
// it will be updated later using the useEffect hook
// or via the join or leave functions below
const [joinedRooms,setJoinedRooms] = useState([]);
// when the component that uses this hook is mounted
// or the user id changes, update the state
useEffect(() => {
let canceled;
setFetching(true);
(async() => {
try {
const rooms = await fetchRooms(userId);
canceled || setJoinedRooms(rooms);
} catch(err) {
canceled || setError(error);
} finally {
canceled || setFetching(false);
}
})();
return () => canceled = true;
},[userId]);
const leave = async roomId => {
try {
await leaveRoom(userId,roomId)
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => joined.filter(r => r !== roomId));
} catch(err) {
// couldn't leave the room - what do you want to do with the state?
}
}
const join = async roomId => {
try {
await joinRoom(userId,roomId);
// alternatively you could fetch all the user rooms again here
setJoinedRooms(joined => [...joined,roomId]);
} catch(err) {
// couldn't join the room - what do you want to do with the state?
}
}
return {
fetching,
error,
joinedRooms,
leave,
join
}
}
In a component you'd use it something like this:
const Rooms = (userId,listOfAllYourRoomIds) => {
const { joinedRooms, fetching, error, join, leave } = useRooms(userId);
// the `join` and `leave` functions are what you'll call
// when a user wants to join or leave a room, the joinedRooms prop will get
// updated according, and everything will "just work"
return listOfAllYourRoomIds.map(roomId => <SomeRoomComponent roomId={roomId}/>)
}

How to call a react hook fetch request in a functional component to access data then pass to a class component to map?

After a huge amount of trial and error for a complex webGL project I have landed on a solution that will reduce the amount of re-engineering working, threejs code (from another developer) and, as this project is extremely time restrained, reduce the amount of time needed. It's also worth noting my experience of this is limited and I am the only developer left on the team.
The project current accepts a large array of random user data, which is exported from a js file and then consumed here...
import Users from "./data/data-users";
class UsersManager {
constructor() {
this.mapUserCountries = {};
}
init() {
Users.forEach(user => {
const c = user.country;
if (!this.mapUserCountries[c])
this.mapUserCountries[c] = { nbUsers: 0, users: [] };
this.mapUserCountries[c].nbUsers++;
this.mapUserCountries[c].users.push(user);
});
}
getUsersPerCountry(country) {
return this.mapUserCountries[country];
}
}
export default new UsersManager();
Here is my fetch request..
import { useState, useEffect } from "react";
const FetchUsers = () => {
const [hasError, setErrors] = useState(false);
const [users, setUsers] = useState({});
async function fetchData() {
const res = await fetch(
"https://swapi.co/api/planets/4/"
);
res
.json()
.then(res => setUsers(res))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);
return JSON.stringify(users);
};
export default FetchUsers;
I have run into lots of issues as the UserManager is a class component and if I import my fetchUsers into this file, call it and save it to a variable like so const Users = fetchUsers(); it violates hooks.
I want to be able to return a function that will return my users from the database as an array.
That will then be able to be passed into the UserManager in the same way the hard coded data is and mapped over to be actioned by LOTS of other files.
I've mocked up a small codesandbox with what the flow would be ideally but I know I need a solution outside of hooks...
https://codesandbox.io/s/funny-borg-u2yl6
thanks
--- EDIT ---
import usersP from "./data/data-users";
class UsersManager {
constructor() {
this.mapUserCountries = {};
this.state = {
users: undefined
};
}
init() {
usersP.then(users => {
this.setState({ users });
});
console.log(usersP);
this.state.users.forEach(user => {
const c = user.country;
if (!this.mapUserCountries[c])
this.mapUserCountries[c] = { nbUsers: 0, users: [] };
this.mapUserCountries[c].nbUsers++;
this.mapUserCountries[c].users.push(user);
});
}
getUsersPerCountry(country) {
return this.mapUserCountries[country];
}
}
export default new UsersManager();
console.log (UsersManager.js:16 Uncaught TypeError: Cannot read property 'forEach' of undefined
at UsersManager.init (UsersManager.js:16)
at Loader.SceneApp.onLoadingComplete [as callback] (App.js:39)
at Loader.onAssetLoaded (index.js:20)
at index.js:36
at three.module.js:36226
at HTMLImageElement.onImageLoad)
I fixed your sandbox example.
You cannot load the users synchronously (using import) as you need to make a http call to fetch the users so it's asynchronous.
As a result you can fetch the users inside the componentDidMount lifecycle method and use a state variable to store them once they are fetched
There are a couple guidelines that will help separate functions that are Hooks and functions that are Components (these are true most of the time):
1 Component functions use pascal case (start with a capital letter) and always return JSX.
2 Custom Hooks functions conventionally begin with the word "use" and never return JSX.
In your case you probably want to make a custom Hooks function that must be called in a component;
function useUserData() {
const [hasError, setErrors] = useState(false);
const [users, setUsers] = useState({});
const networkCall = useCallback(async fetchData = () => {
const res = await fetch(
"https://swapi.co/api/planets/4/"
);
res
.json()
.then(res => setUsers(res))
.catch(err => setErrors(err));
} , [])
useEffect(() => {
fetchData();
}, []);
return {users, hasError};
}
Then call that custom hook in one of your components:
function App() {
const {users, hasError} = useUserData();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>{users}</div>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
If you then need to share that fetched data throughout your app, you can pass it down via props or the context API: https://reactjs.org/docs/context.html
(post a message if you'd like an example of this).

Categories