Cannot setting the state with the data fetched in useEffect -React - javascript

I want to send an API request in useEffect hook and setting the state variables value with the fetched data. I added 2 console.log for detecting the state variables value. I except the second log to be setted with the fetched data, however it still prints null.
Here is my code:
import { useEffect, useState } from "react";
import axios from "axios";
const Test = () =>{
const [users, setUsers] = useState(null);
useEffect(()=>{
const getData = async ()=>{
const resp = await axios.get("https://jsonplaceholder.typicode.com/todos");
console.log(users);
setUsers(resp.data);
console.log(users);
};
getData();
},[])
return (
<div>hello</div>
)
};
export default Test;
Additionally the console output look likes this:
null
null

useState's setter is asynchronous, therefore your second console.log will be called before the users is actually updated.
For it to work, just put it outside the useEffect.
import { useEffect, useState } from "react";
import axios from "axios";
const Test = () =>{
const [users, setUsers] = useState(null);
useEffect(()=>{
const getData = async ()=>{
const resp = await axios.get("https://jsonplaceholder.typicode.com/todos");
console.log(users);
setUsers(resp.data);
};
getData();
},[])
console.log(users);
return (
<div>hello</div>
)
};
export default Test;
or in another dedicated useEffect, by passing users in the dependencies array.
import { useEffect, useState } from "react";
import axios from "axios";
const Test = () =>{
const [users, setUsers] = useState(null);
useEffect(()=>{
const getData = async ()=>{
const resp = await axios.get("https://jsonplaceholder.typicode.com/todos");
console.log(users);
setUsers(resp.data);
};
getData();
},[])
useEffect(()=>{
console.log(users);
},[users])
return (
<div>hello</div>
)
};

Related

React Hook useEffect contains a call to & missing dependencies

So we have a an issue here when running NPM start, this is what I have received warnings when compiling, can't for the life of me figure this one out, this is my first ever question after years of using StackOverflow. I have now included my app.js and index.js, I'm sure i am doing something else wrong too as when I include //eslint-disable-next-line to all pages mentioned the localhost shows a white screen.
React Hook useEffect contains a call to 'setNewDevice'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [finishConfirmation] as a second argument to the useEffect Hook.
const Confirmed = props => {
const [newDevice, setNewDevice] = useState(false);
const [complete, setComplete] = useState(false);
const db = firebase.firestore();
useEffect(() => {
let email = window.localStorage.getItem("confirmationEmail");
if (!email) {
setNewDevice(true);
} else {
finishConfirmation(email);
}
});
React Hook useEffect has missing dependencies: 'moreInfoComplete', 'requestNotifications', and 'userState.userData.firstName'. Either include them or remove the dependency array.
const Dashboard = () => {
const [firstName, setFirstName] = useState(null);
const [lastName, setLastName] = useState(null);
const [moreInfoComplete, setMoreInfoComplete] = useState(false);
const { userState, userDispatch } = useContext(UserContext);
const { sendMessage } = useContext(ToastContext);
const db = firebase.firestore();
useEffect(() => {
if (
(moreInfoComplete || userState.userData.firstName) &&
"Notification" in window &&
Notification.permission === "default"
) {
requestNotifications();
}
}, []);
React Hook useEffect has missing dependencies: 'db', 'userDispatch', and 'userState.userData.pushTokenWeb'. Either include them or remove the dependency array.
const MainRouter = () => {
const [initializationComplete, setInitComplete] = useState(false);
const { userState, userDispatch } = useContext(UserContext);
const userId = userState.userId;
const db = firebase.firestore();
useEffect(() => {
sendPushNotification({
token: userState.userData.pushTokenWeb,
title: "Boop",
body: "shoop"
});
App.js
import React from "react";
import { ToastProvider } from "./contexts/toastContext";
import { UserProvider } from "./contexts/userContext";
import MainRouter from "./MainRouter";
const App = () => {
return (
<ToastProvider>
<UserProvider>
<MainRouter />
</UserProvider>
</ToastProvider>
);
};
export default App;
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();

ReactJS rendering Issue fetching an API

I'm trying to fetch a WeatherApp API, using Geolocation.
My problem is the rendering:
It doesn't allow me to render the page before I fetch (but after I somehow manage to fetch, the code seems to work).
Returning Error message:
Type Error : Cannot Read Property 'temp' of undefined
import React, { useState } from 'react';
import './App.css';
import Axios from 'axios';
function App() {
const [ positionLat, setPositionLat ] = useState('') ;
const [ positionLong, setPositionLong] = useState('') ;
navigator.geolocation.getCurrentPosition(function(position) {
setPositionLat(position.coords.latitude);
setPositionLong(position.coords.longitude);
});
const [weather, setWeather] = useState('');
const fetchingWeather = () => {
Axios.get(
`https://api.openweathermap.org/data/2.5/weather?lat=${positionLat}&lon=${positionLong}&appid={API_KEY}&units=metric`)
.then((res) => {
console.log(res.data.main.temp)
setWeather(res.data)
})
}
// this line is returning the error
console.log(weather.main.temp)
return (
<div className="App">
<p>lattitude :{positionLat}</p>
<p>longitude :{positionLong}</p>
<button onClick={fetchingWeather}>Cliquez moi pour fetch</button>
</div>
);
}
export default App;
Fetching weather and setting weather state is asynchronous, your are console logging weather.main.temp before the request has completed. And fetching data is side effect in reactjs. So you are suggested to fetch weather info by using useEffect hooks and set weather state there.
import React, { useState, useEffect } from 'react';
import './App.css';
import Axios from 'axios';
function App() {
const [ positionLat, setPositionLat ] = useState('') ;
const [ positionLong, setPositionLong] = useState('') ;
navigator.geolocation.getCurrentPosition(function(position) {
setPositionLat(position.coords.latitude);
setPositionLong(position.coords.longitude);
});
const [weather, setWeather] = useState('');
const fetchingWeather = () => {
Axios.get(
`https://api.openweathermap.org/data/2.5/weather?lat=${positionLat}&lon=${positionLong}&appid={API_KEY}&units=metric`)
.then((res) => {
console.log(res.data.main.temp)
setWeather(res.data)
})
}
useEffect(() => {
fetchingWeather();
}, [weather])
return (
<div className="App">
<p>lattitude :{positionLat}</p>
<p>longitude :{positionLong}</p>
<button onClick={fetchingWeather}>Cliquez moi pour fetch</button>
</div>
);
}
export default App;
That should work.

Get logged user every time the component renders under React ContextAPI

I'm using ContextAPI in a small React project, I use HttpOnly Cookie to store the user's token when I hit the /login endpoint.
This is UserContext.js shown bellow, which encapsulates all the components (children) in App.js
import axios from "axios";
import { createContext, useEffect, useState } from "react";
const UserContext = createContext();
const UserContextProvider = ({ children }) => {
const [loggedUser, setLoggedUser] = useState(undefined);
const checkLoggedIn = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/logged-in`);
setLoggedUser(response.data);
};
useEffect(() => {
checkLoggedIn();
}, []);
return (
<UserContext.Provider value={{ loggedUser }}>
{children}
</UserContext.Provider>
);
};
export { UserContext };
export default UserContextProvider;
What I understand is when I log in, I setLoggedUser to the state from the /login response, and now it is available for all the children components of the context.
Now I can navigate to all components wrapped by the context and print for example the email of the loggedUser, but what if the email changed while we're logged in? I'll still see the old email on my components because the data is outdated in the state. And what if token got invalidated on the server while we were logged in.. (The only case we'll get updated data is if I refresh the app because that will trigger useEffect in the context provider and refresh the state again)
Should I also pass the checkLoggedIn function through the context's value property to make it available for other components and then use it in UseEffect in every component? Or is there a better solution for this problem?
After the latest comment if you want to check for email on every re-render then you can remove [] from useEffect as stated above in the comments by #abu dujana.
import axios from "axios";
import { createContext, useEffect, useState } from "react";
const UserContext = createContext();
const UserContextProvider = ({ children }) => {
const [loggedUser, setLoggedUser] = useState(undefined);
const checkLoggedIn = async () => {
const response = await axios.get(`${process.env.REACT_APP_URL}/logged-in`);
setLoggedUser(response.data);
};
useEffect(() => {
checkLoggedIn();
});
return (
<UserContext.Provider value={{ loggedUser }}>
{children}
</UserContext.Provider>
);
};
export { UserContext };
export default UserContextProvider;

Console log showing more than expected while using React Hooks

I am trying to fetch Data from an Api. I am getting the required result but when I try to console log it , it is console logging 4 times.
This is my app.js where I am using the fetchData.
import React, {useEffect, useState} from 'react';
import styles from './App.modules.css';
import {Header, Cards, Footer, Map, Table, Statecards} from './components/exports';
import {fetchData} from './api';
function App() {
const [data, setData] = useState({});
useEffect(() => {
const settingData = async () => {
const data = await fetchData();
setData(data);
}
settingData();
}, []);
console.log(data);
return <div className = {styles.container}>
<Header />
</div>
;
}
export default App;
This is the fetchData function
import axios from 'axios';
const url = 'https://api.covid19india.org/data.json';
export const fetchData = async () => {
try{
const response = await axios.get(url);
return response;
}
catch(err){
console.log(err);
}
};
The console.log in the app.js is giving 4 console logs as below
I am not being able to figure out what's wrong.
const settingData = async () => {
const data = await fetchData();
setData(data);
}
useEffect(() => {
settingData();
}, []);
try this one.

How can I mock return data from a custom hook in React/Jest?

I have a custom hook called useFetch which simply fetches data and returns it, in my component test I want to just mock this hook to return some fake data, how can I go about doing this?
import React, { useEffect, useState } from 'react';
export const useFetch = (url: string) => {
const [data, setData] = useState();
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
setData(json);
} catch (error) {
console.log(error);
}
};
fetchData();
}, [url]);
return data;
};
const App = () => {
const config = useFetch(`/api/url`);
return (
<div></div>
);
};
export default App;
Is there anyway I can mock useFetch so that const config is set to some dummy data in my Jest test?
I Will suggest to put your hook in separate file lets say useFetch.js conntains
import { useEffect, useState } from "react";
export const useFetch = (url: string) => {
const [data, setData] = useState();
useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(url);
const json = await res.json();
setData(json);
} catch (error) {
console.log(error);
}
};
fetchData();
}, [url]);
return data;
};
Keeping your app component file like follows
import React from "react";
import { useFetch } from "./useFetch";
const App = () => {
const config = useFetch(`/api/url`);
return (
<div></div>
);
};
export default App;
with above split you can easily mock your hook, example test file as follows
import React from "react";
import { render } from "#testing-library/react";
import App from "./App";
// mock config
const mockConfig = {
data: "mock data"
};
// this will mock complete file, we have provided mock implementation
// for useFetch function
jest.mock("./useFetch", () => ({
useFetch: () => mockConfig
}));
test("should render with mock useFetch", () => {
const { getByText } = render(<App />);
// test logic goes here
});
Assuming all the files are in same directory.
You could try to mock the fetch library.
In the test setup:
global.fetch = jest.fn()
And then in your test:
global.fetch.mockResolvedValue({ json: () => ({ data: "" })})

Categories