I want to call the function getWeather every time setLocation() is called.How can I achieve it?
useEffect(() => {
fetch(`${IP_API_URL}`)
.then((res)=> res.json()
.then((data)=>{
setLocation(data.country.name)
})
)
return getWeather(location)
})
setLocation will have a state right ???
const [location, setLocation] = useState();
useEffect(() => {
fetch(`${IP_API_URL}`)
.then((res) => res.json()
.then((data) => {
setLocation(data.country.name)
})
)
}, [])
//add another effect
useEffect(() => {
getWeather(location)
}, [location])
Related
I am working on a simple crypto ticker app . I am using an api to get details about cryptos and this is how fetching look likes currently.
const [ Data , setData ] = useState([]);
useEffect(() => {
const interval = setInterval(() => {
fetch('https://api.coindcx.com/exchange/ticker').then((result) => {
result.json().then((resp) => {
console.log(resp)
setData(resp);
console.log(Data)
setLoading(false);
})
})
}, 3000);
return () => clearInterval(interval);
},[])
I am using interval so that data is updated every 3 seconds.
The problem I am facing is that in the useEffect setData is not setting data. console.log(resp) is working correctly but console.log(Data) is loggin out an empty array in console whereas I want data and resp to be the same.
If you want to console.log your data
You need to use useEffect for console.log, because setstate in async and not sync
useEffect(() => {
const interval = setInterval(() => {
fetch('https://api.coindcx.com/exchange/ticker').then((result) => {
result.json().then((resp) => {
console.log(resp)
setData(resp);
console.log(Data)
setLoading(false);
})
})
}, 3000);
return () => clearInterval(interval);
},[])
and than:
useEffect(() =>
{
console.log(data)
}
,[data])
The below function gets the current location of a user:
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
console.log(jsonResponse)
return jsonResponse;
});
};
useEffect(() => {
console.log(getCurrentLocation());
}, []);
logging in useEffect is showing undefined and it is appearing first in the console, then jsonResponse shows next in the console with the corresponding object. Why is that ?
getCurrentLocation doesn't return anything, that's why you got undefined.
Moreover, fetch returns a Promise, which is asynchronous, meaning you don't get the result immediately, you must pass a calback to then to get the result when it is available.
const getCurrentLocation = () => {
return fetch("https://ipinfo.io/json?token=$TOKEN")
.then(response => response.json());
};
useEffect(() => {
getCurrentLocation()
.then(location => console.log(location));
}, []);
The getCurrentLocation function is not returning anything. Try saving the location in the state, so that you can access it when needed:
const [currentLocation, setCurrentLocation] = useState(null);
const getCurrentLocation = () => {
fetch("https://ipinfo.io/json?token=$TOKEN")
.then((response) => response.json())
.then((jsonResponse) => {
setCurrentLocation(jsonResponse); // <- save the location in state
});
};
useEffect(() => {
getCurrentLocation();
}, []);
return <div>{currentLocation}</div>
If you need the location in a useEffect, you could do:
useEffect(() => {
if (currentLocation !== null) {
// ...
}
}, [currentLocation])
you can simply use async/await to get response, take a look at this one:
const getCurrentLocation = async () => {
const result = await fetch("https://ipinfo.io/json?token=$TOKEN");
return result.json();
};
const handleGetLocation = async () => {
const result = await getCurrentLocation();
console.log(result);
};
useEffect(() => {
handleGetLocation();
}, []);
Alternating between the 2 buttons will display first names or last names, but pressing them together really fast will chain requests and will combine the two. How can I make create a check, and only display the names from the button that was pressed last
export default function App() {
const [name, setName] = useState();
return (
<div className="App">
<button onClick={() => setName("first_name")}>1</button>
<button onClick={() => setName("last_name")}>2</button>
<Users name={name} />
</div>
);
}
export default function Users({ name }) {
const [users, setUsers] = useState([]);
useEffect(() => {
setUsers([]);
axios({
method: "GET",
url: `https://reqres.in/api/users?delay=1`
})
.then((res) => {
const allUsers = res.data.data.map((user) => <p>{user[name]}</p>);
setUsers((prev) => [...prev, ...allUsers]);
})
.catch((e) => {
console.log(e);
});
}, [name]);
return <div className="Users">{users}</div>;
}
Here is a great article by Dan Abramov about the useEffect hook in which he also talks about how to handle race cases- https://overreacted.io/a-complete-guide-to-useeffect/#speaking-of-race-conditions
To solve your issue, create a variable like let didCancel = false at the start of useEffect. Then, you have to return a function from useEffect, which automatically runs at the time when the name changes next time. In that function set didCancel to true. Now, you have to handle fetch response only if didCancel is false. This way, you are discarding all fetch responses received from second-last, third-last, etc. button presses, and handling fetch response only from the last button press.
Here is updated useEffect code:-
useEffect(() => {
let didCancel = false;
setUsers([]);
axios({
method: "GET",
url: `https://reqres.in/api/users?delay=1`
})
.then((res) => {
if (!didCancel) {
const allUsers = res.data.data.map((user) => <p>{user[name]}</p>);
setUsers((prev) => [...prev, ...allUsers]);
}
})
.catch((e) => {
console.log(e);
});
return () => {
didCancel = true;
};
}, [name]);
return <div className="Users">{users}</div>;
}
you have to create a loading state, and the user should not be able to send a new request until the data is received... you can create a hook for this or use SWR:
let me give you an example:
function Users(usersList) {
return (
<ul>
{usersList.map((user, key) => (
<li key={key}>{user}</li>
))}
</ul>
);
}
const useFetchUsers = (name) => {
const [isLoading, setIsLoading] = React.useState(true);
const [error, setError] = React.useState(null);
const [data, setData] = React.useState([]);
React.useEffect(() => {
setIsLoading(true);
setError(null);
fetch('https://blahblahblah.com/api/users')
.then((res) => res.json())
.then((response) => setData(response))
.catch((err) => setError(err))
.finally(() => setIsLoading(false));
}, [name]);
return {
isLoading,
error,
data,
};
};
function App() {
const [name, setName] = React.useState('Tom');
const { isLoading, error, data } = useFetchUsers(name);
const handleSubmitName = (name) => {
if (isLoading) alert('wait!');
else setName(name);
};
if (error) return <>an error occured</>;
if (data)
return (
<>
<button onClick={() => handleSubmitName('first_name')}>1</button>
<button onClick={() => handleSubmitName('last_name')}>2</button>
<Users name={name} />
</>
);
}
hint/note: it's just pseudocode and there are some tools to do data fetching + caching.
The problem is in this line setUsers((prev) => [...prev, ...allUsers]);. You are assuming that prev is [], but when the second request is resolve prev has data, that is why you see the request are combined:
I recommend to change your useEffect block to avoid the problem you are facing:
useEffect(() => {
axios({
method: "GET",
url: `https://reqres.in/api/users?delay=1`
})
.then((res) => {
const allUsers = res.data.data.map((user) => <p>{user[name]}</p>);
setUsers(...allUsers); //--> with the last name's value
})
.catch((e) => {
console.log(e);
});
}, [name]);
I am getting infinite requests on my network, and it's due to my useEffect. I know that the problem is because I am putting in the brackets as the second argument the 'posts' and the 'setPost' inside the useEffect function, but I need the page to render whenever I add a new post, so the 'posts' must be inside the brackets.
function Home() {
const {userData, setUserData} = useContext(userContext)
const [posts, setPost] = useState([])
const [createPost, setCreatePost] = useState('')
const handleToken = () => {
localStorage.removeItem('auth-token')
}
const token = localStorage.getItem("auth-token");
const handleOnSubmit = (e) => {
e.preventDefault()
axios.post('http://localhost:5000/posts', {textOfThePost: createPost}, {
headers: { 'auth-token': token },
})
.then((res) => {setCreatePost("")})
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}
useEffect(() => {
}, [posts])
If you're doing setPost inside useEffect, I assume posts being changed, and you've added posts as dependency in useEffect, Of course which will re-call and it goes infinite loop. Make sure when do you want to call posts API.
const [posts, setPost] = useState([])
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, [posts]) // this will trigger useEffect and It goes infinite loop
// Change it to
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data) // Which will change `posts`
})
}, []) -> Which call only one time
This useEffects gets called everytime posts changes, and inside the useEffect you're changing posts value, so you got into an recursive loop.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [posts])
If you want it to get called only once, you should leave the empty array in your effect, so it will only get called once when your component is mounted.
useEffect(() => {
axios.get('http://localhost:5000/posts')
.then(res => {
setPost(res.data)
})
}, [])
I am using useEffect to get data from an api.
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const cats = await response.json();
console.log(cats);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
The problem is when I try to use it in the return, its value is undefined.
{cats.map((data) => {
cats has value when I console.log it.
I cannot use componentDidMount because all my code is functional components.
Edit: I updated the code as per answers below but still get
TypeError: cats.map is not a function
All answers below actually make sense but I am not sure why its not working.
export default function Posts() {
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(setCats)
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
<h4>{data.main}</h4>
})}
</div>
)
}
This is because React renders your screen before finishing to get response from API. When you render screen, variable cats doesn't have values. You can run useEffect after each rendering. You can rerender by changing state from useEffect (This technique is often used). Do not forget to add [] or [cats] as a dependency of useEffect (second params) otherwise you will get infinite loop.
Below code works even when cats === [] or some array.
export default () => {
const [cats, setCats] = useState([])
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const result = await response.json();
setCats(result)
} catch (e) {
}
};
fetchData();
}, []);
return (
<div>
{cats.map(cat => <div>cat</div>)}
</div>)
}
You have to map the cats data into state.
const [cats, setCats] = useState([]);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
`/api/posts/getCats`
);
const data = await response.json();
setCats(data);
} catch (e) {
console.error(e);
}
};
fetchData();
}, []);
You need to
call setCats when the response comes back (right now, you're just logging it)
.map only once cats has been populated:
const [cats, setCats] = useState();
useEffect(() => {
fetch(`/api/posts/getCats`)
.then(res => res.json())
.then(result => setCats(result.cats))
.catch(console.error);
}, []);
return (
<div>
{cats?.map((data) => {
// ...