I wan't to fill the variable outside the axios in array type and then export the array to another file.
navigation.js
import axios from 'axios'
/* eslint-disable */
let menus
axios.get('https://api.link/', {
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` }
})
.then(res => {
menus = res.data
})
/* eslint-enable */
export default menus
index.js
import navMenu from './navigation'
// Array of sections
export default [...navMenu]
api results
[
{
"Oid": "3b05b576-fa95-11eb-84d0-00163e01a21d",
"Name": "Menu",
"Icon": "BookIcon",
"Submenu": [
{
"Oid": "3b05b576-fa95-11eb-84d0-00163e013r46",
"Name": "Submenu",
"Link": "link/to/page",
"Icon": "BookOpenIcon"
}
]
}
]
or maybe give me the solution to export array from api to index.js
I would suggest to wrap your axios call as a function and use its return value rather than exporting the menu variable.
async function makeRequest() {
const response = await axios.get('https://api.link/', {
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` }
})
return response.data
}
and you can call that function as follows:
makeRequest().then((data) => {
//do something with data
}
I am not sure what you are trying to achieve, but there is an example of what I suggested:
App.js
import getDataFromServer from "./navigation";
import { useState } from "react";
function App() {
const [myState, setMyState] = useState([]);
const callServer = async () => {
let result = await getDataFromServer();
setMyState(result.data);
console.log(myState);
};
const listItems = myState.map((item) => <li>{item["title"]}</li>);
return (
<div>
<button onClick={callServer}>Call Server</button>
<ul>{listItems}</ul>
</div>
);
}
export default App;
In another module e.g. (navigation.js)
import axios from "axios";
const url = "https://jsonplaceholder.typicode.com/posts";
export default async function getDataFromServer() {
const result = await axios.get(url);
return result;
}
The example uses sample API from jsonplaceholder site:
const url = "https://jsonplaceholder.typicode.com/posts";
The bottom line, your alternatives are:
If your data reside in an external module, you can follow the
intuition I suggested above.
If you are passing data from a parent component to a child component, you can use props.
If you are passing data from a child component to a parent component, you can use callbacks.
Or you can look into useContext or look into other state
containers (redux) to see how you can share data between the different
components in an app.
Related
I use #nuxtjs/composition-api(0.15.1), but I faced some problems about accessing Vuex getters in computed().
This is my code in composition API:
import { computed, useContext, useFetch, reactive } from '#nuxtjs/composition-api';
setup() {
const { store } = useContext();
const products = computed(() => {
return store.getters['products/pageProducts'];
});
const pagination = computed(() => {
return store.getters['products/pagination'];
});
useFetch(() => {
if (!process.server) {
store.dispatch('products/getPage');
}
});
return {
products,
pagination,
};
}
And the console keeps reporting the warning:
[Vue warn]: Write operation failed: computed value is readonly.
found in
---> <Pages/products/Cat.vue> at pages/products/_cat.vue
<Nuxt>
<Layouts/default.vue> at layouts/default.vue
<Root>
I'm really confused. Because I didn't try to mutate the computed property, just fetching the Data with the AJAX and then simply assign the data to the state in the Vuex mutations.
But I rewrite the code in option API in this way:
export default {
components: {
ProductCard,
Pagination,
},
async fetch() {
if (process.server) {
await this.$store.dispatch('products/getPage');
}
},
computed: {
products() {
return this.$store.getters['products/pageProducts'];
},
pagination() {
return this.$store.getters['products/pagination'];
},
},
};
Everything works fine, there's no any errors or warnings. Is it the way I'm wrongly accessing the getters in the composition API or that's just a bug with the #nuxtjs/composition-api plugin?
fix: computed property hydration doesn't work with useFetch #207
This problem might not can be solved until the Nuxt3 come out.
But I found an alternative solution which use the middleware() instead of use useFetch(), if you want to the prevent this bug by fetching AJAX data with Vuex Actions and then retrieve it by Getters via the computed().
I make another clearer example which it's the same context like the question above.
~/pages/index.vue :
<script>
import { computed, onMounted, useContext, useFetch } from '#nuxtjs/composition-api';
export default {
async middleware({ store }) {
await store.dispatch('getUser');
},
setup() {
const { store } = useContext();
const user = computed(() => store.getters.user);
return {
user,
};
},
}
</script>
~/store/index.js (Vuex)
const state = () => ({
user: {},
});
const actions = {
async getUser({ commit }) {
const { data } = await this.$axios.get('https://randomuser.me/api/');
console.log(data.results[0]);
commit('SET_USER', data.results[0]);
},
};
const mutations = {
SET_USER(state, user) {
state.user = user;
},
};
const getters = {
user(state) {
return state.user;
},
};
If there's something wrong in my answer, please feel free to give your comments.
I was working with Vuex and had a problem that I could solve on my own. The problem was that action I created doesn't return data inside state with method created inside my Vue component. This problem got solved by simply adding return before new Promise.
So problem solved but I don't really understand the difference that made the problem get solved by using return. What does the difference having return makes?
This is my created function which before using return with actions didn't load data on initial loading
created () {
this.$store.dispatch('updateNews')
.then( response => {
this.news = this.$store.getters.getNews
})
.catch( error => this.error = "Error happened during fetching news" );
},
This is my store after adding return
import Vue from "vue";
import Vuex from "vuex";
import axios from 'axios';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
news: []
},
getters:{
getNews(state){
return state.news
}
},
mutations: {
UPDATE_NEWS(state, payload){
state.news = payload
}
},
actions: {
updateNews(context){
var url = 'https://newsapi.org/v2/top-headlines?' +
'country=us&' +
'apiKey=something';
return new Promise ( (res, rej) => {
axios
.get(url)
.then(response => {
context.commit('UPDATE_NEWS', response.data)
res()
})
.catch( error => rej() )
})
}
},
});
a promise doesn't work as a simple declaration inside a function, you have actually return the promise to work with it. The case here is a little bit weird tho, becuase axios already returns a promise to work with. I think the problem is that you want to assing the value of a variable in the state of the store programatically to a variable in component data, when the correct flow for something like that would be accessing that value with a computed property, like this:
Vuex
import Vue from "vue";
import Vuex from "vuex";
import axios from 'axios';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
news: []
},
getters:{
getNews(state){
return state.news
}
},
mutations: {
UPDATE_NEWS(state, payload){
state.news = payload
}
},
actions: {
updateNews(context){
var url = 'https://newsapi.org/v2/top-headlines?' +
'country=us&' +
'apiKey=something';
axios
.get(url)
.then(response => {
context.commit('UPDATE_NEWS', response.data)
})
.catch( error => console.log('Oops, something went wrong with the news', error) );
}
},
});
Component
created () {
this.$store.dispatch('updateNews');
},
computed: {
news() {
return this.$store.getters.getNews;
}
}
Using it like this, you don't have to make a variable called news inside component's data, just the computed property, and access it the same way you would access a variable returned in component's data
Okay, I have two different components and each of those get Axios response. But I don't want to fetch data in each component separate. That's is not right, and it cause components run separate...
Updated 3
I did some changes on the code, but still having some problems. I am doing axios call with Vuex in Store.js and import it into my component. it's like below.
This is my store.js component;
import Vue from "vue";
import Vuex from "vuex";
var actions = _buildActions();
var modules = {};
var mutations = _buildMutations();
const state = {
storedData: []
};
Vue.use(Vuex);
const getters = {
storedData: function(state) {
return state.storedData;
}
};
function _buildActions() {
return {
fetchData({ commit }) {
axios
.get("/ajax")
.then(response => {
commit("SET_DATA", response.data);
})
.catch(error => {
commit("SET_ERROR", error);
});
}
};
}
function _buildMutations() {
return {
SET_DATA(state, payload) {
console.log("payload", payload);
const postData = payload.filter(post => post.userId == 1);
state.storedData = postData;
}
};
}
export default new Vuex.Store({
actions: actions,
modules: modules,
mutations: mutations,
state: state,
getters
});
Now importing it into Average component.
import store from './Store.js';
export default {
name:'average',
data(){
return{
avg:"",
storedData: [],
}
},
mounted () {
console.log(this.$store)
this.$store.dispatch('fetchDatas')
this.storedData = this.$store.dispatch('fetchData')
},
methods: {
avgArray: function (region) {
const sum = arr => arr.reduce((a,c) => (a += c),0);
const avg = arr => sum(arr) / arr.length;
return avg(region);
},
},
computed: {
mapGetters(["storedData"])
groupedPricesByRegion () {
return this.storedData.reduce((acc, obj) => {
var key = obj.region;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj.m2_price);
return acc;
}, {});
},
averagesByRegion () {
let arr = [];
Object.entries(this.groupedPricesByRegion)
.forEach(([key, value]) => {
arr.push({ [key]: Math.round(this.avgArray(value)) });
});
return arr;
},
}
}
I can see the data stored in the console. But there are errors too. I can't properly pass the data in myComponent
https://i.stack.imgur.com/J6mlV.png
if you don't want use vuex to distrib data maybe you can try eventBus, when you get data form the axios respose #emit the event and in another component #on this event
The issue is that
To resolve the error you're getting, Below are the steps.
Import your store file inside the file where your Vue Instance is initialized.
// Assuming your store file is at the same level
import store from './store';
Inside your, Add store object inside your Vue Instance
function initApp(appNode) {
new Vue({
el: appNode,
router: Router,
store // ES6 sytax
});
}
There you go, you can now access your store from any component.
UPDATE: For Second Error
Instead of changing data inside your component, change it inside mutation in the store because you do not want to write the same login in other components where the same method is used.
Hence,
computed: {
...mapGetters(["storedData"]),
anotherFunction() {
// Function implementation.
}
}
Inside your mutation set the data.
SET_DATA(state, payload) {
console.log("payload", payload);
state.storedData = payload;
}
Inside getters, you can perform what you were performing inside your computed properties.
storedData: function(state) {
const postData = state.storedData.filter(post => post.userId == 1);
return postData;
}
Vuex Official docs
Here is the working codesandbox
Hope this helps!
Can anyone see why this wouldn't work please,
Trying to use vuex store to manage my axios requests and transfer to a component as follows:
In my vuex store module I have the following
import axios from "axios";
export const state = () => ({
cases: [],
})
export const mutations = {
listCases (state, cases) {
state.cases = cases;
},
}
export const actions = {
loadCases ({ commit, context }) {
return axios.get('http')
.then(res => {
const convertCases = []
for (const key in res.data) {
convertCases.push({ ...res.data[key], id: key })
}
commit('listCases', convertCases)
})
.catch(e => context.error(e));
},
export const getters = {
// return the state
cases(state) {
return state.cases
}
}
I checked amd my axios request is returning my results as expected and passing to the mutation
In my component I have
import { mapMutations, mapGetters, mapActions } from 'vuex'
export default {
created () {
this.$store.dispatch('cases/loadCases');
},
computed: {
...mapGetters ({
cases: 'cases/cases'
})
},
</script>
Now i assumed based on what I've learnt that i could call with
and this would return my items.
but i get an error cases is not defined,
Anyone abe to tell me my error please
Many Thanks
Take a look here: https://v2.vuejs.org/v2/guide/list.html#Array-Change-Detection
You may be able to make it reactive this way:
export const mutations = {
listCases (state, cases) {
state.cases = [];
cases.forEach((c) => {
state.cases.push(c);
});
},
}
I am trying to fetch some data from an api using Redux. My code looks like this:
Action:
// Import libraries
import axios from 'axios';
// Import types
import {
GET_ALL_PICKS
} from './types';
export const getAllPicks = ({ token }) => {
const getPicks = (dispatch) => {
axios({
method: 'get',
url: 'http://myapi/',
headers: {
Authorization: `Bearer ${token}`
}
})
.then((response) => {
console.log(response.data); // First log here returns data just fine
dispatch({
type: GET_ALL_PICKS,
payload: response.data
});
})
.catch((error) => {
console.log(error);
});
};
return getPicks;
};
Reducer:
// Import types
import {
GET_ALL_PICKS
} from '../actions/types';
// Set Initial State
const INITIAL_STATE = {
allPicks: {},
loading: false,
error: ''
};
// Make pick reducers
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case GET_ALL_PICKS:
return { ...state, allPicks: action.payload }; // Logging action.payload here returns data just fine
default:
return state;
}
};
Component:
// Import Libraries
import React, { Component } from 'react';
import { Text } from 'react-native';
import { connect } from 'react-redux';
import {
getAllPicks
} from '../actions/picks';
// Make Component
class HomeScreen extends Component {
// Fetch Data
componentWillMount() {
const { token } = this.props;
this.props.getAllPicks({ token });
}
// Test response
componentDidMount() {
console.log(this.props.allPicks); // This log returns empty object, why?!
}
render() {
return (
<Text>Test</Text>
);
}
}
const mapStateToProps = ({ auth, picks }) => {
const { token } = auth;
const { allPicks } = picks;
return {
token,
allPicks
};
};
export default connect(mapStateToProps, { getAllPicks })(HomeScreen);
When I run the app I see the data in the action console.log and if I run a console.log(action.payload) in the reducer I see the data just fine but in component I see an empty array which suggests I'm not hooking up the data in my reducer correctly? Here's a screen shot of the logs:
I have also tried this in my reducer after some Googling:
return Object.assign({}, state, {
allPicks: action.payload
});
but again I got the same result. Can anyone explain to me what I am doing wrong?
You are confusing the component lifecycle and the API lifecycle.
In practice, what's happening is:
componentWillMount
getAllPicks
componentDidMount (at which point, the API didn't return, the picks are empty)
[... wait for the API to return]
then the API returns with the data, but too late
What you need to do then is check for your "picks" state in the render() function, which will be updated each time your state changes (which happens when the API returns), thanks to the connect() function.
You can also check that the picks are updated properly using componentWillUpdate, not componentDidMount which again has nothing to do with the props being updated.