For some reason my whole page reloads every time it updates the state after it gets it from the database. The page flickers and I end up at the top of the page. Why is this?
I update the entire state in other functions like sort(), that works perfect without reloading. I have put event.preventDefault() in every click handler so that shouldn't be the problem.
One of the great things with using React is to have a smooth UI without reloading so this is annoying.
function App() {
const [contacts, setContacts] = useState({ items: [] });
useEffect(() => {
axios
.get('http://localhost:5000/')
.then((result) => {
setContacts({ items: result.data });
})
.catch((err) => console.log(err));
}, []);
And this is the function that gets called:
const handleSubmit = (event) => {
event.preventDefault();
if (!id) {
axios
.post('http://localhost:5000/add/', input)
.then(() => {
setInput(emptyState);
})
.catch((err) => console.log(err));
} else {
axios
.post(`http://localhost:5000/update/${id}`, input)
.then(() => {
props.updateContact(input);
setInput(emptyState);
})
.catch((err) => console.log(err));
}
window.location = '/';
};
You need to put something in your [].
You can see that we passed props.name into the array in the second argument. This will now cause the effect to always run again when the name changes.
If you don't pass anything it will always update and will be useless.
useEffect(() => {
document.title = `Page of ${props.name}`
}, [props.name])
Related
So I'm creating a simple MERN App, backend is working properly, but when working with useState hook in frontend is causing issues.
what im trying to do is to fetch "users" data(an array of object with field username) from backend endpoints, and updating the users array which is a hook, but it only updates with the last itm of the incoming username and not list of all usernames!!
code for fetching and updating the hook:
const [users, setUsers] = useState([]);
const getUsers = () => {
fetch("http://localhost:5000/users")
.then(res => res.json())
.then(data => {
console.log(data); //line 17
data.map((itm) => {
console.log([itm.username]) //line 19
setUsers([...users, itm.username])
})
})
.catch(err => console.log(err))
}
useEffect(() => {
getUsers();
}, [])
console.log(users); //line 30
what I want is to get a list of usernames in the "users" state!
something like this:
users = ["spidey", "thor", "ironman", "captain america"]
console.log is also not showing any errors...
console window
pls help, can't figure out where it's getting wrong?
The issue is two-fold, first you are using Array.prototype.map to iterate an array but are issuing unintentional side-effects (the state updates), and second, you are enqueueing state updates in a loop but using standard updates, each subsequent update overwrites the previous so only the last enqueued update is what you see in the next render.
Use either a .forEach to loop over the data and use a functional state update to correctly update from the previous state.
const getUsers = () => {
fetch("http://localhost:5000/users")
.then(res => res.json())
.then(data => {
console.log(data);
data.forEach((itm) => {
console.log([itm.username]);
setUsers(users => [...users, itm.username]);
})
})
.catch(err => console.log(err));
}
Or use the .map and just map data to the array you want to append to the users state.
const getUsers = () => {
fetch("http://localhost:5000/users")
.then(res => res.json())
.then(data => {
console.log(data);
setUsers(users => users.concat(data.map(itm => itm.username)));
})
.catch(err => console.log(err));
}
you can set the map result in a variable after that you can call the useState on it.
const [users, setUsers] = useState([]);
const getUsers = () => {
fetch("http://localhost:5000/users")
.then(res => res.json())
.then(data => {
console.log(data); //line 17
const userNameData = data.map(itm => itm.username)
setUsers(...users, userNameData)
})
.catch(err => console.log(err))
}
useEffect(() => {
getUsers();
}, [])
console.log(users);
I am using gitbeaker to get a project from gitlab API, after fetching the project, I used useState to save the project object, now I want to fetch another API whose URL is in that object, but whenever I try to access that URL, an error appears "Cannot read property 'issues' of undefined".
Here's my code:
const [project, setProject] = useState<any>({});
const api = new Gitlab({
host: "https://example.com",
token: "my token",
});
useEffect(() => {
(async () => {
const projectsPromises = await api.Projects.all().then((allprojects) => {
return allprojects;
});
Promise.all(projectsPromises).then((data) => {
setProject(data.find((element) => element.id === 338));
});
})();
return () => {};
}, []);
console.log(project);
console.log(project._links.issues);
fetch(project._links.issues).then((res) => console.log(res));
console.log(project); gives me {} and after some time it prints the object, that's why when I try to use project._links.issues it is undefined as I think it isn't resolved yet but I don't know how to make it work.
I solved it by fetching the data in the useEffect hook and saving the response of the api in the state so that I can access it later in my code, like that
const [issues, setIssues] = useState<any>([]);
Promise.all(projectsPromises).then((data) => {
const celoProject: any = data.find((element) => element.id === 338);
setProject(celoProject);
const projectIssues = fetch(celoProject._links.issues)
.then((res) => res.json())
.then((allIssues) => {
setIssues(allIssues);
});
});
If someone has a better way or an explanation why I couldn't access it outside the useEffect, please tell me.
Anything inside the useEffect hook will only execute when the page first loads (because you provided an empty array as the second argument). Anything outside of it will execute on every render (every time props or state changes). That is why it logs {} the first time because the effect is asynchronous and hasn't completed before the component is rendered.
You should run the second fetch in the useEffect hook after the first API request completes. Need more information to determine what exactly is happening beyond this.
const [project, setProject] = useState<any>({});
const api = new Gitlab({
host: "https://example.com",
token: "my token",
});
useEffect(() => {
(async () => {
const projectsPromises = await api.Projects.all().then((allprojects) => {
return allprojects;
});
Promise.all(projectsPromises).then((data) => {
const projectResponse = data.find((element) => element.id === 338)
setProject(projectResponse)
fetch(projectResponse._links.issues).then((res) => {
console.log(res)
// Do something with this response
});
});
})();
return () => {};
}, []);
console.log(project);
console.log(project._links.issues);
I have a question about the correct usage with useEffect and setState in React Hooks.
here is my react app that gets a persisted configuration and some other data depending on the config like this:
function App() {
const [config, setConfig] = useState(null);
// some other states
useEffect(() => {
const fetchData = () => {
axios.get("/path/to/config").then(response => {
if (response.status === 200) {
return response.data
}
})
.then(data => {
setConfig(data.config);
// set some other states
})
.catch(error => {
console.log("Error:" + error);
})
}
fetchData();
}, [config]);
return (
<div >
...
</div>
);
}
If I run this App useEffect is instantly called twice one time for first render and then a second time because setConfig(data.config); is called in the axios get success function.
The user has the possibility to change his own config that is done in another request. If he does I want after state config changes that the config and some other data depending on the config is reloaded via this useEffect function.
Now since there is no setstate callback in react hooks I read somewhere I should use useEffect with the state variable in the dependency array.
How can I prevent that my useEffect is called twice right at the beginning?
I have the feeling that I am doing it all wrong.
Thank you in advance
You need to add an if condition in useEffect, because useEffect is called by default on first render.
useEffect(() => {
if(config !== null){
const fetchData = () => {
axios.get("/path/to/config").then(response => {
if (response.status === 200) {
return response.data
}
})
.then(data => {
setConfig(data.config);
// set some other states
})
.catch(error => {
console.log("Error:" + error);
})
}
fetchData();
}
}, [config]);
This is not right!
I guess this can call fetchData infinitely.
I have two useEffect-s. One is used to fetch data from api and save it in the state and second is called only once and it starts listening to websocket event.
In the websocket event handler I log the fetched data but it always has the default value.
Even though fetching data completes successfully and the list is drawn on UI, the value of list is always empty - [].
const [list, setList] = useState([]);
useEffect(() => {
axios.get("https://sample.api.com/get/list")
.then(res => {
setList(res.data);
});
}, [window.location.pathname.split('/')[2]]);
useEffect(() => {
webSocket.on('messageRecieved', (message) => {
console.log(list);
});
}, []);
Your second effect is referencing the initial list value (an empty array) due to closure. This is why useEffect should reference all of its dependencies in its second argument.
But in this case, where you don't want to subscribe to the webSocket event each time the list is updated, you could use React's refs on the list.
const listValue = useRef([]);
const [list, setList] = useState(listValue.current);
When setting the value:
res => {
listValue.current = res.data
setList(listValue.current);
}
And when retrieving the list in a one time fired useEffect:
useEffect(() => {
webSocket.on('messageRecieved', (message) => {
console.log(listValue.current);
});
}, []);
try changing
.then(res => {
to
.then((res) => {
Would clarify if you added console logs to each hook or said if the values are preset in them:
useEffect(() => {
axios.get("https://sample.api.com/get/list")
.then((res) => {
console.log(res.data)
setList(res.data);
});
}, [window.location.pathname.split('/')[2]]);
useEffect(() => {
webSocket.on('messageRecieved', (message) => {
console.log(list);
console.log(message);
});
}, []);
You could also add error catch, just in case:
.catch((error) => {
console.log(error.response)
})
In many of my components, I have to use token from store to get data and represent it (header menu, footer menu, products on page, slider images, etc.). What I am trying to do is to get this data only if I don't have it, but React keeps sending requests every time token changes (as token is dependency), even though I clearly put condition and I can see it if I console.log it. What am I doing wrong?
const [cities, setCities] = useState([]);
useEffect(() => {
if (!cities.length) {
fetch(`.....&token=${props.token}`)
.then(response => response.json())
.then(data => {
if (data.data.results) {
setCities(data.data.results.cities)
}
})
}
}, [props.token, cities.length]);
The cities will be empty on first render anyway, so you don't need to check for its length and specify it as a dependency:
const [cities, setCities] = useState([]);
useEffect(() => {
fetch(`.....&token=${props.token}`)
.then(response => response.json())
.then(data => {
if (data.data.results) {
setCities(data.data.results.cities)
}
})
}, [props.token]);
You can also memoize the token to prevent it from triggering the useEffect callback:
const token = useMemo(() => props.token, []);
// EDITED BECAUSE OF THE COMMENTS
// should be outside of the function
let timesExecuted = 0
function fetch () {
useEffect(() => {
if(props.token){
timesExecuted = timesExecuted + 1
}
if (timesExecuted === 1) {
fetch(`.....&token=${props.token}`)
.then(response => response.json())
.then(data => {
if (data.data.results) {
setCities(data.data.results.cities)
}
})
}
}, [props.token]);
}
SO IT WILL COME every time BUT BE EXECUTED ONLY WHEN prop.token IS OKEY (feel free to modify the first IF based on token validations).