I would like to check if there's anyone that could show a implementation of a component that is included from a route page (+page.svelte) where the component itself is responsible for fetching the data and handling that trough an endpoint specific to that component.
Previously I have included the data into the component and fetched it in parent component but I guess it must be possible for the component itself to fetch the data.
The aim is for example to have implementation inside the component itself like
<Mycomponent>
instead of
<Mycomponent {myArray}>
I we take this code snippet as an example. This is similar to what I do but I need to use POST method API fetch call.
<script lang="ts">
async function getDataFromAPI() {
const body = {
action: 'search',
};
const res = await fetch('https://externalAPI.com', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});
const values = await res.json();
return values;
}
let promise = getDataFromAPI();
</script>
{#await promise }
<p>...waiting</p>
{:then data}
<p>{data.length} my data </p>
{:catch error}
<p style="color: red">{error.message}</p>
{/await}
The problem seems to be that in my case data is undefined even if when I console.log(values) in this case there's the correct data information.
So I end up in the catch error case and get error 'Failed to fetch'.
It's definitely possible. Basically, two ways to go about it. First is to do the fetch (or call a function imported from somewhere else that does the fetch) in the script tag of the +page.svelte file you want to use it in, and then refer to the variable you stored the fetched data in within the template.
The other way to do it moves the fetch into a separate file. I'm not sure of the pros and cons of this, as I'm still fairly new to svelte myself.
You want to create a +page.js/+page.ts file with an exported load function that fetches the needed data and returns the data in an object.
Then, in your +page.svelte file, you export a variable named data. The data variable will contain the object that your load function returned.
When I get the chance, I'll hack up a quick demonstration and include the code here.
Related
I have a list of products that I can get in my site using a simple function inside a server component.
async function getData() {
const res = await fetch(`${apiPath}`);
const data = (await res.json()) as PackProps[];
return data
}
The user of the site can apply some filters to the list. I already got the new API format that I have to call with the right new parameters, but I don't now how to call it.
The issue is that the filter selection is written inside a client component, which is separated from the component that has the role to show the list.
If i try to nest the components and to pass the parameters I need, then I have another problem:
... cannot be used as a JSX component. Its return type 'Promise<Element>' is not a valid JSX element.
Of course, I cannot call an API and mark my function component as async, inside a client component.
Can you give me some hints on how can I manage to call an API (or maybe force an api to re-render), without losing the benefits of Server Side Rendering?
This is my first attempt at building a web app with Vuejs. I've been trying to get data from an external JSON API and display it on my app. The JSON fetch etc is working fine. but I can't get the data to be displayed reactively on my component.
As you can read in Appraisal.js given an API link some data is populated in Appraisal.app_data. The data always has an array called items (that's just how the API is. I'll add validation later). As a proof of concept I'm trying to display the number of elements in the items array.
Since other components in my app will also use this data, I'm using an external store as the data source everywhere. One of the components calls Appraisal.setLink() on getting some user input. That part is working as expected. However the DOM contents don't change at all.
I referred to State Management for setting up the external store. I also referred to some other answers on StackOverflow with a similar issue and got the following suggestions:
The data should be initialized to undefined or null instead of {} for reactivity to work.
Properties of objects are not reactive. But by my understanding this was changed in Vue3 where it doesn't matter because proxies are in use. Either way I tried using the Object.assign({}, ..., ...) method but it did not help.
Arrow functions cannot be used in methods for reactive objects. If I remove the arrow function and put the body inside .then(function(data) {...}) it complains that this is not defined for the second then function on fetch
// --- src/components/AppraisalView.vue
<script setup>
import ItemView from './ItemView.vue';
</script>
<template>
<div v-if="app_data">{{app_data.items.length}} items in appraisal</div>
<div v-else>Enter link to get quote</div>
</template>
<script>
import {Appraisal} from '../stores/Appraisal.js';
export default {
data() {
return {
app_data: Appraisal.app_data,
}
},
}
</script>
// ---- src/store/Appraisal.js
import {reactive} from 'vue'
import {BuybackCalculator} from './BuybackCalculator.js';
export const Appraisal = reactive({
link: '',
app_data: undefined,
methods: {
setLink(value) {
if (this.link == value) return;
this.link = value;
console.log('Updating appraisal with: '+this.link);
fetch(this.link+".json")
.then((response) => response.json())
.then((data) => {
this.app_data = data;
console.log(this.app_data);
BuybackCalculator.methods.calculate_buyback(this.app_data);
});
}
}
});
Requirement:
I require to make multiple API calls and then output the result onto the React Front-End.
I am using React hooks and not classes.
Explanation of steps taken:
In the view function (Function_V), I use a button to call a function (call it Function_A and it acts as the Service function), sending the parameters to that function. Function_A uses Axios to make a get call with the parameters and returns the required promise and the response content. I push parts of the returned output from Function_A onto a local array variable (in order to make a final response object). Taking few outputs from this Axios call, I make a function call to another function (Function_B) in Function_A's Axios call. This is another Axios call to get the rest of the required API response.
I successfully make the final response object in Function_A (utilizing both the outputs from Function_A and Function_B).
Finally, Function_A returns my custom response object as a promise.
Problems and things I tried:
I get the required response in the model function as a promise, but am unable to copy it to model function usual variables.
--> I tried making async and await (not good with the async-await and then-catch concept).
--> I created a local state in the model function and tried copying the response.
These are not working.
My issue is I cannot find a way to traverse the returned promise to access/store the response values in the local variables or set the local states with these values.
Below is the dummy code, please let me know if I am doing something wrong and how can I fix it.
Thank You All for taking out the time and helping me out! :)
Dummy Code:
View.js
import {Function_A} from ...;
function Function_V(){
e.preventDefault();
const [details, setDetails] = useState("");
async function search(e){
const r = await Function_A(param1,param2);
console.log(r);
setDetails(r);
}
return(
<div>
<form onSubmit={search}>
<button type="submit"> Get User Address Details</button>
</form>
{details.length>1 &&(
<div>{details}</div>
</div>
);
}
Service.js
import {Fucntion_B} from ...;
export async function Function_A(param1,param2){
let url=param1+param2;
let response={};
e.preventDefault();
let myAnswers=[];
let address;
const respon = await axios.get(url);
address=respon.address;
const w= await Function_B(address);
for(let s=0;s<w.length;s++){
myAnswer.push(w[s]);
}
respon={myAnswer,respon.address};
return respon;
}
Helper.js
export async function Function_B(param1_address){
let url=param1_address;
let count;
const r2= await axios.get(url);
count=r2.number;
return count;
}
The way you are using async-await is not correct. You are using it in a promise way which we should not. That is one of the reasons async-await was introduced to make users feel like synchronous calls.
export async myFun(){
const addrResponse = await axios.get(url);
const countResponse await axios.get(`${url}?param=${addrResponse.address}`)
const number = countResponse.number;
}
This is the ideal way, in case you want to create new array for setting state just use addrResponse or countResponse directly.
Solution:
The errors were arising due to the fact that Axios requests were taking place later that the rendering of the components, the response data was not getting passed onto the original function in object format.
Reason - ASYNC-AWAIT usage was done incorrectly. Upon correcting it will fix the error. The code in the question is fixed now.
Note:
Make sure to create a state in the view.js and set it to the response.
This way the result will be stored in the state and available for rendering.
I have a reusable component that I have created on the components folder where I have all the details from the user that logs in to the system which is a header section.
I am trying to use getInitialProps using fetch with isomorphic-unfetch.
static async getInitialProps(ctx) {
const UserdetailsRespone = await fetch("my API url");
const UserdetailsJson = await UserdetailsRespone.json();
return { UserDetails: UserdetailsJson.root[0] };
}
In the render method when I log this.props.UserDetails I get undefined.
It is the same API fetch as the one I am doing in the pages folder where I am able to fetch an API response. But I am not able to fetch the response in the components folder.
Can someone help me to solve it?
Thanks in Advance.
getInitialProps runs server side and client side. So you will have to be careful how you use it. I usually use typeof Window === 'undefined' to check whether getInitialProps is being called server side or not.
If you have a child component that you need to make a call everytime it is mounted, why not stick with componentDidMount?
async componentDidMount() {
const userDetailsResponse = await fetch("my API url");
const userDetailsJson = await userDetailsResponse.json();
this.setState({userDetails: userDetailsJson})
}
You can access the properties from state instead of props then.
If you're using getInitialProps in a child component, it won't work. It only works in the default export of every page.
From official docs
Edit:
As mentioned by Uzair Ashraf, using fetch is the way to go for fetching data in components. You can take a look at swr too.
Background
I have an array of television program objects. The objects contain the name, network, and image for the program. On each item in the array, I have a heart button. The heart allows the user to add the program to a watchlist. Each program is created with a watchlist attribute which is set to false. Clicking the button will toggle the watchlist attribute to true or false.
Troubleshooting Done So Far
I know my update case statement involves returning the following items:
1) Make a copy of my current state;
2) Make a copy of my current programs array;
3) Pass in my updated program.
I'm using debugger to find out what data is being passed from my dispatch to my reducer. Then, I have used another debugger inside my reducer to find out how I should manipulate that data to return my updated state and re render the DOM.
I tried following along with this blog but I didn't quite understand how to apply what was said to my own project.
Code Samples
Here is the callback method in my Program component. It is passing the programId and watchlistValue. I have confirmed that these are the correct values by using console.log to check.
handleWatchlist = (programId, watchlistValue) => {
this.props.toggleWatchlist(programId, watchlistValue)
}
If you would rather see the entire file you can find it here
Here is my toggleWatchlist action. My fetch is successfully updating my database, but I fear I am dispatching incorrectly in my last .then statement.
export function toggleWatchlist(programId, watchlistValue){
console.log('programId: ', programId)
console.log('watchlistValue: ', watchlistValue)
// debugger
// used debugger to figure out what to send as the TOGGLE_WATCHLIST payload
return (dispatch) => {
dispatch({type: 'START_TOGGLE_WATCHLIST_REQUEST'})
fetch(`http://localhost:3000/api/v1/programs/${programId}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
watchlist: !watchlistValue
})
})
.then(res => res.json())
.then(res => dispatch({type: 'TOGGLE_WATCHLIST', id: programId}))
}
}
Here is my programReducer case statement. I know that action.watchlist contains my new watchlist value.
case 'TOGGLE_WATCHLIST':
console.log('inside TOGGLE_WATCHLIST case');
console.log("use debugger to figure out what your 'payload' is here (or whatever you are passing in)");
// debugger
// used debugger to figure out how to pass in updated item to programs array.
const toggledProgram = state.programs.map(program => program.id === action.programId)
// action.watchlist contains new watch value. it is the one thing needing change. However, do I just update the entire program? It will include watchlist value.
return {...state, programs: [...state.programs, toggledProgram]}
If you need to see the entire file, you can find it here.
In addition to those code issues, I have been plagued throughout the course of this project by console errors that indicate my cards need a key prop. I have tried passing in the program.id, the program.title, and simply passing index into my .map error function. All to the same result. Am I placing the key on the proper part of my card component? Full code file here
<Grid columns='six' divided='vertically'>
<Grid.Row >
{props.programs.map((program) => <Program key={program.name}
program={program} />)}
</Grid.Row>
</Grid>
Recap of Questions
Am I dispatching my action properly after my fetch call? I know that if I am sending incorrect info to my reducer than I will not be able to update my state correctly.
Have I placed my key value on the correct part of my card component?
Thank you for your time and attention!