useEffect and state management library - reactjs

I have this problem that I am trying to understand and solve. I want to fetch data in the main component and allow editing of this data. The problem is that the first time the data is properly loaded into the state but if I go back to the previous page where the table is and enter to edit another record then until I re-render the page the data from the previous record are in the state. I use Zustand to pass data between components. Please help guys :(
const { id } = useParams<string>();
const setData1 = useStore((state) => state.setData1)
const setData2 = useStore((state) => state.setData2)
React.useEffect(() => {
async function fetchData() {
const response = await getExampleData(id);
setData1(response.name);
setData2(response.values);
}
fetchData();
}, [id]);
return(
<ComponentData1/>
<ComponentData2/>)
And in i.e. ComponentData1 i'm using this below.
const data1 = useStore((state) => state.data1)

Related

usestate can change the state value after axios in useEffect

I expected to get the url with category=business,but the web automatically reset my state to the url that dosent have the category.I dont know the reason behind
let {id}=useParams()
const [newsurl,setNewsurl]=useState(()=>{
const initialstate="https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
return initialstate;})
//console.log(id);
const [articles, setActicles] = useState([]);
useEffect( ()=>{
if(id === 2)
console.log("condition")
setNewsurl("https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee")},[])
useEffect(() => {
const getArticles = async () => {
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state ha
s been updated.
}, [newsurl])
//return "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";}
return (<><Newsnavbar />{articles?.map(({title,description,url,urlToImage,publishedAt,source})=>(
<NewsItem
title={title}
desciption={description}
url={url}
urlToImage={urlToImage}
publishedAt={publishedAt}
source={source.name} />
)) } </>
)
one more things is that when i save the code the page will change to have category but when i refresh it ,it change back to the inital state.Same case when typing the url with no id.May i know how to fix this and the reason behind?
Setting the state in React acts like an async function.
Meaning that the when you set the state and put a console.log right after it, it will likely run before the state has actually finished updating.
You can instead, for example, use a useEffect hook that is dependant on the relevant state in-order to see that the state value actually gets updates as anticipated.
Example:
useEffect(() => {
console.log(newsurl)
// Whatever else we want to do after the state has been updated.
}, [newsurl])
This console.log will run only after the state has finished changing and a render has occurred.
Note: "newsurl" in the example is interchangeable with whatever other state piece you're dealing with.
Check the documentation for more info about this.
setState is an async operation so in the first render both your useEffetcs run when your url is equal to the default value you pass to the useState hook. in the next render your url is changed but the second useEffect is not running anymore because you passed an empty array as it's dependency so it runs just once.
you can rewrite your code like the snippet below to solve the problem.
const [articles, setActicles] = useState([]);
const Id = props.id;
useEffect(() => {
const getArticles = async () => {
const newsurl =
Id === 2
? "https://newsapi.org/v2/top-headlines?country=de&category=business&apiKey=c75d8c8ba2f1470bb24817af1ed669ee"
: "https://newsapi.org/v2/top-headlines?country=us&apiKey=c75d8c8ba2f1470bb24817af1ed669ee";
const res = await Axios.get(newsurl);
setActicles(res.data.articles);
console.log(res);
};
getArticles();
}, []);

re-updating the page state

There are 2 pages: the first loads an array of posts with a jsonplaceholder, the second displays the body of a specific post by clicking on the post.
When I return from the post page to the page with all the posts, the data is updated and re-requested.
How can I save the page state?
const [data, setData] = useState([])
const [posts, setPosts] = useState([])
const getPostsData = async () => {
try {
const postsData = await getData('https://jsonplaceholder.typicode.com/posts')
setData(postsData)
} catch (error) {
console.log(error.message)
}
}
useEffect(() => { getPostsData() }, [])
useEffect(() => { setPosts(data) }, [data])
posts.map(post => <Link to={`${post.id}`} key={post.id}><li className="list-group-item" >{post.title}</li></Link>)
I use 2 states. One is for loading data, and the second is for displaying. I need it for sorting and searching.
I think for your case it's better to use redux , context or react-query, but if you don't want to, you must avoid using Link for the child component because when you change route your previous state will be removed
you can simply show post detail as a modal or part of the main page

How to share data from useState in React to another Component

Hey guys let me quickly explain my problem.
I currently have Component in which User can Search something. And after they Click on a Button I get the Data from Firebase which is then stored in a useState which I map afterwards. This whole operation is in one function.
But for now I show the result at the same Page because I dont know how to transfer the data in the useState to the other component.
const handleClick = async () => {
const kurzRef = collectionGroup(db, 'kurzwaffensub' );
const MOD = query(kurzRef,where("kurzModell", "==", `${kurzModell}` ));
if(kurzModell) {
const getWaffenDaten = async () => {
const modell = await getDocs(MOD);
const data = [];
for (const doc of modell.docs) {
const parentDoc = await getDoc(doc.ref.parent.parent);
const { Name, avatar,avatarPath, Erfahrung, Adresse, Schützenverein } = parentDoc.data();
const waffenbilderRef = collection(db, 'users', doc.data().uid, 'waffenbildersub')
const subCollectionDocs = await getDocs(waffenbilderRef)
const subCollectionData = subCollectionDocs.docs.map((doc) => {
return { id: doc.id, ...doc.data()}
})
data.push({
...doc.data(),
Name,
subCollectionData,
avatar,
avatarPath,
Erfahrung,
Adresse,
Schützenverein
});
}
setTest(data)
}
getWaffenDaten()
}
After that operation I just return the Result in the same Page . And I want to change the page after the onClick event with the Result. Because I dont want to see the User Interface of the Search Component.
Perhabs its pretty simple but Im still a beginner and would be very glad if you can help me out and teach me something new and important.
You can do this in multiple ways:
You can pass search query as URL parameter if you using router and fetch the data from result page
You can use state management tool like Redux or built in context api.

React state is empty inside useEffect

Currently I'm building a pusher chat app with react. I'm trying to keep a list of online users. I'm using this code below:
const [users, setUsers] = useState([]);
useEffect(() => { // UseEffect so only called at first time render
window.Echo.join("server.0")
.here((allUsers) => {
let addUsers = [];
allUsers.map((u) => {
addUsers.push(u.name)
})
setUsers(addUsers);
})
.joining((user) => {
console.log(`User ${user.name} joined`);
setUsers([users, user]);
})
.leaving((user) => {
console.log(`User ${user.name} left`);
let addUsers = users;
addUsers.filter((u) => {
return u !== user.name;
})
setUsers(addUsers);
})}, []);
Whenever I subscribe to the pusher channel, I receive the users that are currently subscribed and the state is set correctly. All subscribed users are showing. However when a new user joins/leaves, the .joining/.leaving method is called and the users state is empty when I console log it. This way the users state is being set to only the newly added user and all other users are being ignored. I'm new to react so there is probably a simple explanation for this. I was not able to find the answer myself tough. I would really appreciate your help.
I saw the problem in joining. You need to update setState like this: setUsers([...users, user.name]);
And leaving also need to update:
const addUsers = users.filter((u) => {
return u !== user.name;
});
setUsers(addUsers);
here should also rewrite:
let addUsers = allUsers.map((u) => u.name);
setUsers(addUsers);
I found the issue. The problem is that when accessing state from within a callback funtion, it always returns the initial value. In my case an empty array. It does work when using a reference variable. I added the following lines:
const [users, _setUsers] = useState([]);
const usersRef = React.useRef(users);
const setUsers = data => {
usersRef.current = data;
_setUsers(data);
}
Each time I update the users state, I use the setUsers function. Now when I acces the state from inside my callback function with usersRef.current I get the latest state.
Also I used the code from the answer of #Viet to update the values correctly.

Correct way to update react component from secondary source

I'm kind of new with React Hooks and I've encountered a problem when making a component. My App has a simple form with a few fields and a "Calculate" button which fetches info from an API and displays the results on a table. The app uses two currencies, they can be switched with a pair of buttons. What I want is to update the table(re fetch the data) when currency is changed, but only is there was already something calculated via the main "Calculate" button before changing the currency. My component is something along the lines of:
const ProductionCosts = () => {
const [data, setData] = useState({});
const [useXCurrency, setUseXCurrency] = useState(true);
const calcCosts = useCallback(async () => {
fetchCalcData(args);
}, [args]);
useEffect(() => {
if (Object.keys(data).length > 0) //check data isn't empty, hence it was already calculated
fetchCalcData();
}, [useXCurrency]);
return (
......
);
};
Doing something similar to the above works, but the linter will say that data needs to be in the dependency list of the useEffect, but adding it will result on a loop given that fetchCalcData modifies data and triggers the effect, I DO know that the linter suggestions aren't absolute, but at the same time I know that there must be a better way. So besides adding Boolean flags or something like that, there is a better approach to this case?
Typically you want to use a refenence with the initial value and update it on success, on next useEffect the condition will be falsy:
const ProductionCosts = () => {
const [data, setData] = useState({});
const dataRef = useRef(data);
useEffect(() => {
if (Object.keys(dataRef.current).length > 0) {
const data = // fetch your data
dataRef.current = data;
}
}, [useXCurrency]);
return <></>;
};

Resources