Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetching data from api before render component

I'm sending 2 api requests before render the page:

const Profile = {
    template: '#profile',
    attributes: null,
    photos: [],
    data: function () {
        return {attributes: Profile.attributes, photos: Profile.photos};
    },
    beforeRouteEnter: function (to, from, next) {
        function getProfile() {
            return axios.get('user/get-profile?access-token=1', {responseType: 'json'});
        }
        function getPhotos() {
            return axios.get('photos?access-token=1', {responseType: 'json'});
        }

        axios.all([getProfile(), getPhotos()])
            .then(axios.spread(function (profile, photos ) {
                console.log(profile, photos );
                next(vm => {
                    vm.setProfile(profile);
                    vm.setPhotos(photos);
                })
            }));
    },
    methods: {
        setProfile: function (response) {
            Profile.attributes = response.data;
            console.log(Profile.attributes);
        },
        setPhotos: function (response) {
            Profile.photos = response.data;
            console.log(response);
        },           
    }
};

The problem is rendering occures before setProfile and setPhotos methods. How to correct render my component?

like image 957
Alex Avatar asked Sep 11 '17 12:09

Alex


People also ask

How fetch data from API to functional component?

Put the fetchData function above in the useEffect hook and call it, like so: useEffect(() => { const url = "https://api.adviceslip.com/advice"; const fetchData = async () => { try { const response = await fetch(url); const json = await response. json(); console. log(json); } catch (error) { console.

How call API before render React functional component?

There are no correct ways to make API call before component rendered from the same component. You may preferred make API call in parent component and render presentation component when and only when have consumable data.

How wait for data before rendering React?

To wait for a ReactJS component to finish updating, we use a loading state in our react application by use of the conditional rendering of the component. This can be achieved by the use of the useState and useEffect hooks in the functional components.

How do you call API before render React native?

So one solution is to use hooks as a separate component in your class. And then in your js class you have access to all lifecycle methods. There is a method shouldComponentUpdate(state, props) It takes props and states and you can compare if you want to re-render screen or not.It will call right before rendering.


2 Answers

As mentioned in the comments, the first async/await solution is a little bit misleading, because all lifecycle methods are synchronous. This works, because the code is transpiled to an synchronous function with an IIFE inside.

Below I have added a few more recent snippets.

Old answer

Try it with async/await. I've removed beforeRouteEnter, axios.spread and added create.

const Profile = {
  template: '#profile',
  attributes: null,
  photos: [],
  data() {
    return {
      attributes: null,
      photos: null,
    };
  },
  async created() {
    const getProfile = await axios.get('user/get-profile?access-token=1');
    const getPhotos = await axios.get('photos?access-token=1');

    this.setProfile(profile);
    this.setPhotos(photos);
  },
  methods: {
    setProfile(response) {
      this.attributes = response.data;
      console.log(this.attributes);
    },
    setPhotos(response) {
      this.photos = response.data;
      console.log(response);
    },
  },
};

Shorter

const Profile = {
  template: '#profile',
  attributes: null,
  photos: [],
  data() {
    return {
      attributes: null,
      photos: null,
    };
  },
  async created() {
    this.attributes = await axios.get('user/get-profile?access-token=1');
    this.photo = await axios.get('photos?access-token=1');
  },
};

Updated answer

You can use an async function inside your lifecycle method.

const Profile = {
  template: '#profile',
  attributes: null,
  photos: [],
  data() {
    return {
      attributes: null,
      photos: null,
    };
  },
  created() {
    const fetchData = async () => {
      const { data: attributes } = await axios.get(
        'user/get-profile?access-token=1'
      );
      const { data: photos } = await axios.get('photos?access-token=1');

      this.attributes = attributes;
      this.photos = photos;
    };

    fetchData();
  },
};

Vue 3 and setup()

In Vue 3 you can use async setup(). If you use this, you must wrap your component wit Suspense. Caution! This API is currently experimental https://v3.vuejs.org/guide/migration/suspense.html.

<Suspense>
  <template #default>
    <YourComponent />
  </template>
  <template #fallback>
    <div>Loading ...</div>
  </template>
</Suspense>
export default {
  name: 'YourComponent',
  async setup() {
    const { data: attributes } = await axios.get('user/get-profile?access-token=1');
    const { data: photos } = await axios.get('photos?access-token=1');

    return {
      attributes,
      photos
    }
  }
}
like image 172
Phil Avatar answered Sep 26 '22 15:09

Phil


You should just be able to return the Promise that is returned from calling axios.all like:

return axios.all([getProfile(), getPhotos()])
// .then() => ...

Or you could add a property to the data object and use this to show a loader until all Promises have resolved

const Profile = {
    template: '#profile',
    attributes: null,
    photos: [],
    data: function () {
        return {attributes: Profile.attributes, photos: Profile.photos, isLoading: true};
    },
    beforeRouteEnter: function (to, from, next) {
        function getProfile() {
            return axios.get('user/get-profile?access-token=1', {responseType: 'json'});
        }
        function getPhotos() {
            return axios.get('photos?access-token=1', {responseType: 'json'});
        }

        axios.all([getProfile(), getPhotos()])
            .then(axios.spread(function (profile, memes) {
                console.log(profile, memes);
                this.isLoading = false

                next(vm => {
                    vm.setProfile(profile);
                    vm.setPhotos(photos);
                })
            }));
    },
    methods: {
        setProfile: function (response) {
            Profile.attributes = response.data;
            console.log(Profile.attributes);
        },
        setPhotos: function (response) {
            Profile.photos = response.data;
            console.log(response);
        },           
    }
};

Template code omitted, but you can just switch the content you display based on the isLoading. If you go down this route then probably best to create an abstraction for the loader.

I would also suggest you might want to look at vuex rather than coupling all of your data to any specific component state.

like image 40
Purple Hexagon Avatar answered Sep 24 '22 15:09

Purple Hexagon