useEffect re-render the same result from a supabase query - reactjs

I'm working with a user's bookmarks list. So, when I tried to render it doing the queries inside useEffect, it renders the result each time.
const [posts, setPosts] = useState([])
useEffect(() => {
async function getBookmarks() {
await supabase
.from('bookmarks')
.select('post_id')
.match({ user_id: user.id })
.then(res => {
res.data.map(async (bk) => {
await supabase
.from('post')
.select('id,description,songlink,created_at,author,profiles:author(username)')
.match({ id: bk.post_id })
.single()
.then(res => setPosts(posts => [...posts, res.data]))
})
})
}
getBookmarks()
}, [])
I don't want to get the same result twice or more, but I don't know what I'm doing wrong.

it might be helpful to get a better understanding of what you're wanting it to do vs what it's actually doing for people to help answer this better. What I'm seeing here in the code is you asking for data and then asking again. Knowing the data structure of your tables could help, and I'm not sure that you need to be doing multiple fetch calls here.
https://supabase.com/docs/reference/javascript/select
if your tables are joined, you should be able to just do something like
.from('bookmarks').select(`post_id:post_id ( id, description, songlink, created_at )

You might want to use useCallback hook. Didn't try the code but you can get the idea:
const [posts, setPosts] = useState([])
const getBookmarks = useCallback(async ()=>{
await supabase
.from('bookmarks')
.select('post_id')
.match({ user_id: user.id })
.then(res => {
res.data.map(async (bk) => {
await supabase
.from('post')
.select('id,description,songlink,created_at,author,profiles:author(username)')
.match({ id: bk.post_id })
.single()
.then(res => setPosts(posts => [...posts, res.data]))
})
})
},[])
useEffect(() => {
getBookmarks()
}, [])

Related

How to re-run useEffect after a submit function?

Hello Guys!
So in my Project, I do a fetch data function in useeffect but when I add a new element to the firestore I want that the useEffect to run again so in the list will contain the added element, somebody can give me advice on how can I do it ?
useEffect(() => {
if (session) {
fetchTodos();
}
}, [session]);
const fetchTodos = async () => {
const fetchedtodos = [];
const querySnapshot = await getDocs(collection(db, session.user.uid));
querySnapshot.forEach((doc) => {
return fetchedtodos.push({ id: doc.id, data: doc.data() });
});
setTodos(fetchedtodos);
};
const submitHandler = async (todo) => {
const data = await addDoc(collection(db, session.user.uid), {
todo,
createdAt: serverTimestamp(),
type: "active",
});
}
I want that when I run the submitHandler the useeffect run again so the list will be the newest
The only way to get a useEffect hook to run again, is to change something in the dependency array, or to not provide an array at all, and get the component to re-render (by changing props or state). See useEffect Documentation
You could just call fetchTodos directly after you call addDoc:
const submitHandler = async (todo) => {
const data = await addDoc(collection(db, session.user.uid), {
todo,
createdAt: serverTimestamp(),
type: "active",
});
return fetchTodos();
}
In my experience, the best way to do what you are trying to do is to have any requests that modify your data in the backend return the difference and then modify your state accordingly:
const submitHandler = async (todo) => {
const data = await addDoc(collection(db, session.user.uid), {
todo,
createdAt: serverTimestamp(),
type: 'active',
});
setTodos((prev) => [...prev, data]);
};
This way you don't have to do any large requests for what is mostly the same data within the same session.
Of course, this method is not ideal if multiple clients/users can modify your backend's data, or if you do not control what the endpoint responds with.
Hope this helps.

Using a POST request's response values in React Native

everyone. I'm a new developer and this is my first post here, so please bear with me.
What I'm trying to do is access the values that I received from a POST request that I made.
Below you can see what I'm trying to do. I set up a state where I'll store my data and then there's a function called fetchData that does a POST request and receives an object with the values, right?
Now I that I've received my object, I want to store its values in some of my variables.
Since the values have been stored in the 'data' state, I thought I would access its values by doing something like 'userName = data.userName', or something like 'age = data.userAge'. Obviously that didn't work out because my IDE does not know those values yet lol. So how do you think I should access them?
const [data, setData] = useState([{}]);
useEffect(() => {
fetchData({
ids: [0],
})
.then((response: any) => {
setData(response);
})
.catch((error: any) => {
console.log('error', error);
});
}, []);
dont place the function directly in useEffect, do something like this instead
const [data, setData] = useState([])
const getData = async() => {
try{
const response = await fetchData({ids: [0]});
setData(response);
}catch(error){
console.log(error);
}
}
useEffect(() => {
getData();
},[])

Avoid duplication in fetch call

I'm trying to avoid duplication of data using fetch.
The fetch call looks like this:
const [data, setData] = useState(null);
useEffect(() => {
const promises = urls.map((url) =>
fetch(baseUrl + url).then((response) => {
if (response.ok) return response.json();
throw response
})
);
Promise.all(promises)
.then((json) => setData(json))
}, []);
return { data}
};
export default useFetchAll;
How can the fetch call be modified to avoid duplication ?
Thank you!
Look into SWR or React Query. Both of those libraries will give you a ton of features, including caching data.

users array hook is not updating with all the list items

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);

React, Get Weather Aync Await Errors

Still teaching myself React, and was working on a project when I ran into an issue.
I have the below code.
const Weather = (props) => {
const [weather, setWeather] = useState([]);
const [lat, setLat] = useState([]);
const [long, setLong] = useState([]);
useEffect(() => {
const fetchData = async () => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
await fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`
)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
};
fetchData();
}, [lat, long]);
return (
<React.Fragment>
<div className={styles.weather}>
<div className={styles.weather__current}>{weather.current.temp}</div>
<div className={styles.weather__hourly}>Hourly</div>
<div className={styles.weather__daily}>5 Day</div>
</div>
</React.Fragment>
);
};
export default Weather;
My issue is that my request goes out twice before it has the lat and long "populated", on the third attempt it goes out just fine. So I get 2 responses of bad request, with the third being what I want.
My understanding is that in an Async function, the await, will "pause" until all previous variables are "satisfied". I may be misunderstanding that.
I can "hard code" the values, and everything is fine.
Any help would be appreciated.
The issue is that you are setting two of the useEffect's dependency inside the useEffect function itself (lat and long). You need to separate your useEffect into two separate ones: the first to fetch lat and long, the second to fetch the weather.
useEffect(() => {
navigator.geolocation.getCurrentPosition(function (position) {
setLat(position.coords.latitude);
setLong(position.coords.longitude);
});
}, []);
useEffect(() => {
fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
}, [lat, long]);
Alternatively, since you are not rendering lat and long, you could just make them normal variables, not state variables:
useEffect(() => {
navigator.geolocation.getCurrentPosition(function (position) {
const lat = position.coords.latitude);
const long = position.coords.longitude);
fetch(`https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${long}&appid=SomeID&units=imperial&exclude=minutely`)
.then((res) => res.json())
.then((result) => {
setWeather(result);
console.log(result);
});
});
}, []);
Your request is going twice because you are setting lat and long states before setting the weather and as you know everytime you set/change a state in a component, react updates it again.
The solution is to use useRef hook instead of useState for lat and long. You can also use const variable to store temporarily if not using these values anywhere else.

Resources