I have the following code, that loads a random user from an API. On load, it shows the first name of the fetched user.
Now I want to fetch a new user when clicking the 'Generate user' button, but it's not triggering the useEffect
.
I thought I could trigger a refresh by passing the generate
var to the dependency array in the useEffect
, but it's not working. Should I make generate
part of the state? That seems counter-intuitive for a simple toggle.
Any help / tips appreciated!
import { useEffect, useState } from "react";
const Async = () => {
let generate = false;
const [user, setUser] = useState({name: ''});
useEffect(() => {
fetch('https://randomuser.me/api/?results=1')
.then(response => response.json())
.then(data => setUser({...user, ['name']: data.results[0].name.first}))
.catch(error => console.error(error));
}, [generate]);
function newUser()
{
generate = !generate;
}
return <>
<button onClick={newUser}>Generate user</button>
<div>User {user.name}</div>
</>;
};
CodePudding user response:
Use your Generate With State as Below...
export default function App() {
const [user, setUser] = useState({name: ''});
const [generate,setGenerate] = useState(false)
useEffect(() => {
fetch('https://randomuser.me/api/?results=1')
.then(response => response.json())
.then(data => setUser({...user, ['name']: data.results[0].name.first}))
.catch(error => console.error(error));
}, [generate]);
function newUser()
{
setGenerate(!generate)
}
return <>
<button onClick={newUser}>Generate user</button>
<div>User {user.name}</div>
</>;
}
CodePudding user response:
You could move generate
into a React state that is toggled and triggers a rerender, but it would be better to just abstract the fetch
call into a callback that is called by the button
element's onClick
handler. Call the function once in a mounting useEffect
hook.
import { useEffect, useState } from "react";
const Async = () => {
const [user, setUser] = useState({ name: '' });
const generate = () => {
fetch('https://randomuser.me/api/?results=1')
.then(response => response.json())
.then(data => setUser(user => ({
...user,
name: data.results[0].name.first
})))
.catch(error => console.error(error));
};
// Call once when the component mounts
useEffect(() => {
generate();
}, []);
return (
<>
<button onClick={generate}>Generate user</button>
<div>User {user.name}</div>
</>
);
};
CodePudding user response:
Move your request outside or into a reusable function
const reqData = () => {
// return fetch req
}
const Comp = () => {
useEffec(() => {
reqData(); // assign it or whatever you need
}, [])
const handleClick = useCallback(reqData);
return (
<>
<button onClick={handleCallback}>Generate user</button>
<div>User {user.name}</div>
</>
)
}
This is just psudo code, you will have to set up arguments etc to get it to work, but this will allow you to make more calls per click while also having the content access to the initial load call.
CodePudding user response:
Instead of calling api in useEffect you can call it inside a function
import { useEffect, useState } from "react";
const Async = () => {
let generate = false;
const [user, setUser] = useState({name: ''});
useEffect(() => {
newUser()
}, []);
function newUser()
{
fetch('https://randomuser.me/api/?results=1')
.then(response => response.json())
.then(data => setUser({...user, ['name']: data.results[0].name.first}))
.catch(error => console.error(error));
}
return <>
<button onClick={newUser}>Generate user</button>
<div>User {user.name}</div>
</>;
};
And if you want to go with your code only then put genrate as state like
import { useEffect, useState } from "react";
const Async = () => {
let [generate, setGenerate] = useState(true);
const [user, setUser] = useState({name: ''});
useEffect(() => {
fetch('https://randomuser.me/api/?results=1')
.then(response => response.json())
.then(data => setUser({...user, ['name']: data.results[0].name.first}))
.catch(error => console.error(error));
}, [generate]);
function newUser()
{
setGenerate(!generate)
}
return <>
<button onClick={newUser}>Generate user</button>
<div>User {user.name}</div>
</>;
};