Sveltekit & {#await} - javascript

I don't understand what I'm doing wrong here.
I have a file called activitylog.js and inside it is this
export async function getActivityLogList() {
const resultList = await pb.collection('activitylog').getList(1, 50, {
filter: `user.id="${pb.authStore.model?.id}"`
});
return resultList.items;
}
and inside my +page.svelte I call it using
{#await getActivityLogList()}
<p>Loading...</p>
{:then log}
<p>LOG: {log.length}</p>
{#each log as logs}
<p>{logs.name}</p>
{/each}
{:catch error}
<p>Error: {error.message}</p>
{/await}
But it always shows "Loading..."? The function getActivityLogList() definitely returns an array of items from pocketbase? so why isn't the :then being triggered once the promise is fulfilled

Related

using Getserversideprops inside page to fetch data from strapi, still getting undefined as the data

so i am trying to fetch data from strapi backend using getServerSideprops in nextjs but the data i am getting is undefined even tho the link works just fine inside browser, and yes i am fetching inside a page not inside a component using same method as described in docs what i am doing wrong ?
function Products({props}) {
console.log(props); //<-- returns undefined
return (
<div className=''>
<div>
</div>
</div>
);
}
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`http://localhost:1337/api/products?populate=*`)
const data = await res.json()
console.log(data) //<-- returns undefined
// Pass data to the page via props
return { props: { data } }
}
export default Products;
You're passing data into your component via getServerSideProps, so you should destructure accordingly:
function Products({data}) {
console.log(data);
...
}
You can also log your full props object like so:
function Products(props) {
console.log(props);
...
}

Returning a component from a map function

I want to create a function that maps throw a firestore collection and return a Component for each document in the collection I have tried to use the code below
<div className="posts">
{
db.collection("posts")
.get()
.then((snapshot) => {
snapshot.docs.map((doc)=>(
<PostContect img={doc.data().image} Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title} />
))})}
</div>
but this error shows:
Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.ode
JSX is not the place to make Ajax calls. Instead make the call inside componentDidMount/useEffect and set the data received inside state. As soon as state is updated with received data, React will automatically re-render the component and display the expected content. Try something as follows:
const [snapshots, setSnapshots] = useState();
useEffect(()=> {
db.collection("posts")
.get()
.then((snapshot) => {
setSnapshots(snapshot.docs)
}
)
}, []);
render
<div className="posts">
{snapshots && snapshots.map((doc)=>(
<PostContect img={doc.data().image} Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title} />
)
}
</div>
Since you haven't given a hint as to whether you're using hooks or not, here is how you should deal with asynchronous stuff and setting states in React (using hooks) :-
function MyComponent () {
const [snapshot,setSnapshot] = useState();
useEffect(()=>{
async function getPosts(){
let snapshot = await db.collection("posts").get();
setSnapshot(snapshot);
}
getPosts();
},[])
return
(<div className="posts">
snapshot?.docs?.map((doc)=>(
<PostContect img={doc.data().image} Admin={doc.data().admin} Date={"January 14, 2019"} CommentsNo={"2"} Title={doc.data().title} Body={doc.data().title} />)
</div>)
}
What you are doing is returning Promise object as the error states. That async stuff is taken care either inside a useEffect,event handler or a custom-hook. There could be more ways but this is a general point to start.
As the error message states Promise is not a valid React child. To solve that issue you can introduce a state which keeps the result of db.collection('posts').get() method. Then iterating through on that array with your .map() which will be a valid React child.
See a possible solution for your scenario:
// in the root of you component, introduce a state
const [posts, setPosts] = useState([])
// here in useEffect you can do your API call and update the state
useEffect(() => {
db.collection("posts")
.get()
.then((snapshot) => {
setPosts(snapshot.docs)
})
}, [])
// ... rest of your component's code
return (
<div className="posts">
{
posts && posts.map((doc, i) => (
<div key={i}>
doc.data().title
</div>
// here you can use <PostContect /> component
// either doc.data().title or doc.title to get the value
))
}
</div>
)
Suggested reads:
Using the Effect Hook
Using the State Hook
The code you wrote returns a promise, which can't be rendered by react. You can use conditional rendering to return something else while the data is being fetched.
Check this question for more info

get data from promise

I have some rest api, and need to get data from promise and render it.
const data = http.get(url) // it return the promise.
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: Array(4)
I can console log needed data:
const data = http.get(url).then(res=> console.log(res.data)) // array in console
I read some tutorials, and allways, always it shows how to console log results, but I dont need it in concole, I need render it in component. It may be simple question, but im total new in JS.
const ItemsList = () => {
const data = http.get(url) //return promise. How can I get array from thid promise result?
return (
<ul>
{ data.map(item => {
return (
<ItemPreview key={item.id} item={item} />
);
})}
</ul>
)
}
export default ItemsList
tried async/await. spend already 4 hours to this task, but get only errors. Just don't kwnow how to write it. can someone help, and show me how to do it. Or give me link where I can find how to render promise result in component?
async/await is the way to go
const ItemsList = async () => {
const data = await http.get(url)
return (
<ul>
{ data.map(item => {
return (
<ItemPreview key={item.id} item={item} />
);
})}
</ul>
)
}

React custom hook: can't get an async function

I've got a button that calls an async function, that is returned by a call to a custom React hook, alongside with a reactive prop that I need to keep track of.
CodeSandbox here.
// useEmail.js
import { useState } from "react";
export default function useEmail(message) {
const [returnedMessage, setReturnedMessage] = useState("old");
const send = async () => {
// fake fetch
const whatever = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
setReturnedMessage("new");
};
return {
returnedMessage,
send
};
}
And this is the app
// app.js
import React from "react";
import useEmail from "./useEmail";
export default function App() {
const { returnedMessage, send } = useEmail();
const run = async () => {
console.log("returnMessage PRE", returnedMessage);
await send();
console.log("returnMessage POST", returnedMessage);
};
return (
<div className="App">
<h2>Click and wait for 1 second</h2>
<button onClick={run}>Click me</button>
<h2>Returned message:</h2>
<p>{returnedMessage}</p>
<button onClick={() => window.location.reload()}>
Reload to test again
</button>
<p>
It prints "new", but logs "old"
<br />
even if I await send()...?
</p>
</div>
);
}
useEmail returns both a returnMessage string, that is initialized as "old", and an async function send that fetches something, then flips the returnMessage and sets it to "new".
How is it possible that in the <p>{returnedMessage}</p> the value correctly turns from "old" to "new", while the Console logs always "old", even if I await when calling send()?
It seems like send() is not really treated as an asynchronous function – I've tried in different ways but I always have a correctly updated rendering but a wrong value when I need it in the function for further processing.
Thank you for your help
You can do the job using useRef.
It seems you can't access the updated value without running the hook again.
With useRef you'll get a reference and you can access the data at any time, without running the hook again.
// useEmail.js
export default function useEmail(message) {
const messageRef = React.useRef("old");
const send = async () => {
// fake fetch
const whatever = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
messageRef.current = "new";
};
return {
messageRef,
send
};
}
// app.js
export default function App() {
const { messageRef, send } = useEmail();
const run = async () => {
console.log("returnMessage PRE", messageRef.current);
await send();
console.log("returnMessage POST", messageRef.current);
};
return (
<div className="App">
<h2>Click and wait for 1 second</h2>
<button onClick={run}>Click me</button>
<h2>Returned message:</h2>
<p>{returnedMessage}</p>
<button onClick={() => window.location.reload()}>
Reload to test again
</button>
<p>
It prints "new", but logs "old"
<br />
even if I await send()...?
</p>
</div>
);
}
You have 2 async functions in your custom hook.
Your fetch (which one you await)
setState
So even if you await for the fetch, your setState is still asynchronous:
console.log("returnMessage PRE", returnedMessage); //old
Fetch
Await fetch to complete
Fetch complete
trigger setState
function send() returns undefined (because no return is defined)
console.log("returnMessage POST", returnedMessage); //old
State is updated (async setState is complete)
returnedMessage is updated
Component re-renders
If you want to have actions depending on when returnedMessage is changed, you'll have to use useEffect in your component
useEffect(() => {
if (returnedMessage === "old") return; // Do nothing here
// returnedMessage !== "old" so assume it's "new"
// Do something...
}, [returnedMessage]);
It is a normal behaviour setState will produce only a single re-render at the end of the event even if you used await, try to add a console.log inside your component you will see returnedMessage moved to 'new'
// app.js
import React from "react";
import useEmail from "./useEmail";
export default function App() {
const { returnedMessage, send } = useEmail();
console.log("returnMessage POST", returnedMessage); // in last render it will be new so it will change the view
const run = async () => {
console.log("returnMessage PRE", returnedMessage);
await send();
};
return (
<div className="App">
<h2>Click and wait for 1 second</h2>
<button onClick={run}>Click me</button>
<h2>Returned message:</h2>
<p>{returnedMessage}</p>
<button onClick={() => window.location.reload()}>
Reload to test again
</button>
<p>
It prints "new", but logs "old"
<br />
even if I await send()...?
</p>
</div>
);
}
One thing that I noted, from your custom React Hook, you are returning an async function.
which is this:
async () => {
// fake fetch
const whatever = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
setReturnedMessage("new");
};
And within your App Component, you are accessing the custom hook where send is pointing to this async function. Right?
Now when you are calling your async function you are trying to do:
await send();
Why await here again, since we already have an await inside of our function.
When you do this you are basically waiting for a promise() here, since every async function returns a promise even when nothing is returned.
I feel the implementation of custom hook should change or calling the hook has to be different.
On top of this setState() is itself an asynchronous action. That is not in our control to tell when the state will update :)

Can't await promise in map method

I'm trying to render a list of rooms and then get the user data of each recipient in the room, and since there only can be 2 users in the room (you + the other person), the recipient is just the other person.
async renderRooms(room, user) {
const recipentId = room.users.filter(id => id != user.id)[0],
recipients = await userService.getOne(recipentId);
console.log(recipients)
return (
<ListGroupItem tag="a" href="#" data-unread="3" action key={room._id} onClick={this.onRoomOpened.bind(this, room._id)}>
<img src={""} />
<div className="details">
<div className="recipient-name"></div>
<div className="last-message">
<MessageCircle className="feather" />
This is an example last message...
</div>
</div>
</ListGroupItem>
);
}
render() {
if(this.props.rooms.length < 1) return <LoadingPage />;
return (
<userContext.Consumer>
{({user}) => {
if(this.state.roomId) return <ActiveRoom id={this.state.roomId} />;
else
return (
<ListGroup className="recipients">
{this.props.rooms.map(room => this.renderRooms(room, user))}
</ListGroup>
);
}}
</userContext.Consumer>
);
}
This way I filter out my own user ID from the user array, and then I take the remaining ID and use a promise to get the user data for it. But the problem here is that whenever I call await it throws the error:
react-dom.development.js:14748 Uncaught Error: Objects are not valid
as a React child (found: [object Promise]). If you meant to render a
collection of children, use an array instead.
If I try to use .then() it actually doesn't seem to be called at all, as in, if I put a console.log in there, it prints out nothing.
Any idea how I can accomplish my task?
You can't call an async function in the render method.
Async functions returns a Promise object, so this
{this.props.rooms.map(room => this.renderRooms(room, user))}
Will return an array of Promise Objects and you can't render and Object.
You need to call your async function in componentDidMount and render it when the data is loaded.
Check this answer for an example of what to do.

Categories

Resources