I have a data and I put it in the state and I want to add a new value in addition to the content of the data in the object called watched, but this is a problem, thank you for your help.
import React, { useEffect, useState } from "react";
import ListManager from "./component/manager";
const App = () => {
const [getMovies, setMovie] = useState([]);
const [getLoading, setLoading] = useState(true);
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setMovie(response);
setLoading(false);
});
});
useEffect(() => {
return () => {
setMovise(
getMovies.map((item) => ({
id: item.id,
text: item.name,
rate: item.rate,
watched: false,
}))
);
};
});
if (getLoading) {
return "Please wait...";
}
return <ListManager movies={getMovies} />;
};
export default App;
You don't need the second useEffect, you should use the first useEffect to do all your stuff, also you should pass an empty array to useEffect in order to be executed one time.
import React, { useEffect, useState } from "react";
import ListManager from "./component/manager";
const App = () => {
const [getMovies, setMovie] = useState([]);
const [getLoading, setLoading] = useState(true);
useEffect(() => {
fetch("http://my-json-server.typicode.com/bemaxima/fake-api/movies")
.then((response) => response.json())
.then((response) => {
setMovie(response.map((item) => ({
id: item.id,
text: item.name,
rate: item.rate,
watched: false,
})));
setLoading(false);
});
}, []);
if (getLoading) {
return "Please wait...";
}
return <ListManager movies={getMovies} />;
};
export default App;
It looks like you have a typo calling setMovise instead of setMovies.
I'm not sure about your second useEffect. A return in useEffect is often used
for a cleanup function like this (from ReactJS documentation):
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
I'd just put it normally in the code outside the hook before the return statements.
Your variable naming doesn't fit the usual convention for
useState.
const [getMovies, setMovie] = useState([]);
should be
const [movies, setMovies] = useState([])
Related
React code
import React, { useEffect, useState } from "react";
import { getDocs, collection } from "firebase/firestore";
import { auth, db } from "../firebase-config";
import { useNavigate } from "react-router-dom";
function Load() {
const navigate = useNavigate();
const [accountList, setAccountList] = useState([]);
const [hasEmail, setHasEmail] = useState(false);
const accountRef = collection(db, "accounts");
Am i using useEffect correctly?
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
emailCheck();
direct();
}, []);
checking whether email exists
const emailCheck = () => {
if (accountList.filter((e) => e.email === auth.currentUser.email)) {
setHasEmail(true);
} else {
setHasEmail(false);
}
};
Redirecting based on current user
const direct = () => {
if (hasEmail) {
navigate("/index");
} else {
navigate("/enterdetails");
}
};
return <div></div>;
}
The code compiles but doesn't redirect properly to any of the pages.
What changes should I make?
First question posted excuse me if format is wrong.
There are two problems here:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
emailCheck();
direct();
}, []);
In order:
Since getAccounts is asynchronous, you need to use await when calling it.
But even then, setting state is an asynchronous operation too, so the account list won't be updated immediately after getAccounts completes - even when you use await when calling it.
If you don't use the accountList for rendering UI, you should probably get rid of it as a useState hook altogether, and just use regular JavaScript variables to pass the value around.
But even if you use it in the UI, you'll need to use different logic to check its results. For example, you could run the extra checks inside the getAccounts function and have them use the same results as a regular variable:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
const result = data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}));
setAccountList(result);
emailCheck(result);
direct();
};
getAccounts();
}, []);
const emailCheck = (accounts) => {
setHasEmail(accounts.some((e) => e.email === auth.currentUser.email));
};
Alternatively, you can use a second effect that depends on the accountList state variable to perform the check and redirect:
useEffect(() => {
const getAccounts = async () => {
const data = await getDocs(accountRef);
setAccountList(
data.docs.map((doc) => ({
...doc.data(),
id: doc.id,
}))
);
};
getAccounts();
});
useEffect(() => {
emailCheck();
direct();
}, [accountList]);
Now the second effect will be triggered each time the accountList is updated in the state.
code
import React from "react";
import useUsersData from "./useUsersData";
export const User = ({ _id, name, age, email }) => {
const [userData, setUserData] = useUsersData();
const handleDelete = (id) => {
const proceed = window.confirm("Are you sure?");
if (proceed) {
const url = `http://localhost:5000/users/${id}`;
fetch(url, {
method: "DELETE",
})
.then((res) => res.json())
.then((data) => {
console.log(data);
}); }
const remaining = userData.filter((userData) => userData._id !== id);
console.log(userData);
console.log(remaining);
setUserData(remaining);
console.log(userData);
};
return (
<p onClick={() => handleDelete(_id)}>
X
</p>
</div>
);
};
the custom hook:
import { useEffect, useState } from "react";
const useUsersData = () => {
const [userData, setUserData] = useState([]);
useEffect(() => {
fetch("http://localhost:5000/users")
.then((res) => res.json())
.then((data) => setUserData(data));
}, []);
return [userData, setUserData];
};
export default useUsersData;
not updating useState() custom hook when I use setState(somedata) the custom hook useState does not get updated. I don't know what I did wrong.
You can see in the console log that set user data is not changing.
here is a screenshot
console.log
Whenever you call the dispatch function of useState, its effects on the state will only be noticeable on the next function call.
So if you console.log or otherwise try to use the state immediately after a dispatch, it will not have the latest data.
If you want to use the latest state as soon as it is updated, you need to useEffect while passing the state in the dependency array.
const Example = () => {
const [userData, setUserData] = React.useState(['1', '2', '3']);
const handleDelete = () => {
const remaining = userData.filter((id) => id !== '3');
console.log(userData); // will print ['1', '2', '3']
console.log(remaining); // will print ['1', '2']
setUserData(remaining);
console.log(userData); // will still print ['1', '2', '3']
};
React.useEffect(() => {
console.log('useEffect', userData); // will print "useEffect ['1', '2']"
}, [userData]);
return <p onClick={handleDelete}>X</p>;
};
This is the same issue as posted in The useState set method is not reflecting a change immediately
any idea why this custom hook with SWR causes an infinite loop?
export const useOrganization = () => {
const [data, setData] = useState<OrganizationModel | undefined>();
const { organizationId } = useParams();
const { data: dataSWR } = useSWRImmutable<
AxiosResponse<Omit<OrganizationModel, 'id'>>
>(`organizations/${organizationId}`, api);
useEffect(() => {
if (dataSWR?.data && organizationId) {
setData({ id: organizationId, ...dataSWR.data });
console.log({ id: organizationId, ...dataSWR.data });
}
});
return data;
};
I need to fetch data from API and add missing id from URL param. If I use setData(dataSWR.data), everything is fine. The problem occurs when setData({...dataSWR.data}) is used -> loop.
You need to use useEffect based on the scenario. When dataSWR changed the useEffect call again with new data.
You can add the dataSWR as dependencies argument in useEffect hook.
useEffect(() => { do something... }, [dataSWR])
Example:
export const useOrganization = () => {
const [data, setData] = useState<OrganizationModel | undefined>();
const { organizationId } = useParams();
const { data: dataSWR } = useSWRImmutable<AxiosResponse<Omit<OrganizationModel, 'id'>>>(`organizations/${organizationId}`, API);
useEffect(() => {
if (dataSWR?.data && organizationId) {
setData({ id: organizationId, ...dataSWR.data });
console.log({ id: organizationId, ...dataSWR.data });
};
},[dataSWR]);
return data;
};
Usage of hook:
const data = useOrganization()
Dependencies argument of useEffect is useEffect(callback, dependencies)
Let's explore side effects and runs:
Not provided: the side-effect runs after every rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs after EVERY rendering
});
}
An empty array []: the side-effect runs once after the initial rendering.
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Runs ONCE after initial rendering
}, []);
}
Has props or state values [prop1, prop2, ..., state1, state2]: the side-effect runs only when any dependency value changes.
import { useEffect, useState } from 'react';
function MyComponent({ prop }) {
const [state, setState] = useState('');
useEffect(() => {
// Runs ONCE after initial rendering
// and after every rendering ONLY IF `prop` or `state` changes
}, [prop, state]);
}
I found the solution - useMemo hook:
export const useOrganization = () => {
const { organizationId } = useParams();
const { data } = useSWRImmutable<
AxiosResponse<Omit<OrganizationModel, 'id'>>
>(`organizations/${organizationId}`, api);
const result = useMemo(() => {
if (data && organizationId) {
return { id: organizationId, ...data.data };
}
}, [data, organizationId]);
console.log('useOrganization');
return result;
};
Can someone explain what am I'm doing wrong?
I have a react functional component, where I use useEffect hook to fetch some data from server and put that data to state value. Right after fetching data, at the same useHook I need to use that state value, but the value is clear for some reason. Take a look at my example, console has an empty string, but on the browser I can see that value.
import "./styles.css";
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
function fetchHello() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Hello World");
}, 1000);
});
}
const handleSetValue = async () => {
const hello = await fetchHello();
setValue(hello);
};
useEffect(() => {
const fetchData = async () => {
await handleSetValue();
console.log(value);
};
fetchData();
}, [value]);
return (
<div className="App">
<h1>{value}</h1>
</div>
);
};
export default App;
Link to codesandbox.
The useEffect hook will run after your component renders, and it will be re-run whenever one of the dependencies passed in the second argument's array changes.
In your effect, you are doing console.log(value) but in the dependency array you didn't pass value as a dependency. Thus, the effect only runs on mount (when value is still "") and never again.
By adding value to the dependency array, the effect will run on mount but also whenever value changes (which in a normal scenario you usually don't want to do, but that depends)
import "./styles.css";
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
function fetchHello() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Hello World");
}, 1000);
});
}
const handleSetValue = async () => {
const hello = await fetchHello();
setValue(hello);
};
useEffect(() => {
const fetchData = async () => {
await handleSetValue();
console.log(value);
};
fetchData();
}, [value]);
return (
<div className="App">
<h1>{value}</h1>
</div>
);
};
export default App;
Not sure exactly what you need to do, but if you need to do something with the returned value from your endpoint you should either do it with the endpoint returned value (instead of the one in the state) or handle the state value outside the hook
import "./styles.css";
import React, { useEffect, useState } from "react";
const App = () => {
const [value, setValue] = useState("");
function fetchHello() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("Hello World");
}, 1000);
});
}
const handleSetValue = async () => {
const hello = await fetchHello();
// handle the returned value here
setValue(hello);
};
useEffect(() => {
const fetchData = async () => {
await handleSetValue();
};
fetchData();
}, []);
// Or handle the value stored in the state once is set
if(value) {
// do something
}
return (
<div className="App">
<h1>{value}</h1>
</div>
);
};
export default App;
I would like to get global information from Github user and his repos(and get pinned repos will be awesome). I try to make it with async await but It's is correct? I've got 4 times reRender (4 times console log). It is possible to wait all component to reRender when all data is fetched?
function App() {
const [data, setData] = useState(null);
const [repos, setRepos] = useState(null);
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(`https://api.github.com/users/${username}`);
const respRepos = await axios(`https://api.github.com/users/${username}/repos`);
setData(respGlobal.data);
setRepos(respRepos.data);
};
fetchData()
}, []);
if (data) {
console.log(data, repos);
}
return (<h1>Hello</h1>)
}
Multiple state updates are batched but but only if it occurs from within event handlers synchronously and not setTimeouts or async-await wrapped methods.
This behavior is similar to classes and since in your case its performing two state update cycles due to two state update calls happening
So Initially you have an initial render and then you have two state updates which is why component renders three times.
Since the two states in your case are related, you can create an object and update them together like this:
function App() {
const [resp, setGitData] = useState({ data: null, repos: null });
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(
`https://api.github.com/users/${username}`
);
const respRepos = await axios(
`https://api.github.com/users/${username}/repos`
);
setGitData({ data: respGlobal.data, repos: respGlobal.data });
};
fetchData();
}, []);
console.log('render');
if (resp.data) {
console.log("d", resp.data, resp.repos);
}
return <h1>Hello</h1>;
}
Working demo
Figured I'd take a stab at it because the above answer is nice, however, I like cleanliness.
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const Test = () => {
const [data, setData] = useState([])
useEffect(() => {
(async () => {
const data1 = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
const data2 = await axios.get('https://jsonplaceholder.typicode.com/todos/2')
setData({data1, data2})
})()
}, [])
return JSON.stringify(data)
}
export default Test
Using a self invoking function takes out the extra step of calling the function in useEffect which can sometimes throw Promise errors in IDEs like WebStorm and PHPStorm.
function App() {
const [resp, setGitData] = useState({ data: null, repos: null });
useEffect(() => {
const fetchData = async () => {
const respGlobal = await axios(
`https://api.github.com/users/${username}`
);
const respRepos = await axios(
`https://api.github.com/users/${username}/repos`
);
setGitData({ data: respGlobal.data, repos: respGlobal.data });
};
fetchData();
}, []);
console.log('render');
if (resp.data) {
console.log("d", resp.data, resp.repos);
}
return <h1>Hello</h1>;
}
he made some mistake here:
setGitData({ data: respGlobal.data, repos: respGlobal.data(respRepos.data //it should be respRepos.data});
For other researchers (Live demo):
import React, { useEffect, useState } from "react";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";
function MyComponent(props) {
const [error, setError] = useState("");
const [data, setData] = useState(null);
const [repos, setRepos] = useState(null);
useEffect(() => {
console.log("mount");
const promise = CPromise.from(function* () {
try {
console.log("fetch");
const [respGlobal, respRepos] = [
yield cpAxios(`https://api.github.com/users/${props.username}`),
yield cpAxios(`https://api.github.com/users/${props.username}/repos`)
];
setData(respGlobal.data);
setRepos(respRepos.data);
} catch (err) {
console.warn(err);
CanceledError.rethrow(err); //passthrough
// handle other errors than CanceledError
setError(err + "");
}
}, []);
return () => {
console.log("unmount");
promise.cancel();
};
}, [props.username]);
return (
<div>
{error ? (
<span>{error}</span>
) : (
<ul>
<li>{JSON.stringify(data)}</li>
<li>{JSON.stringify(repos)}</li>
</ul>
)}
</div>
);
}