Vuex and Axios in asyncData - javascript

I want to access my Vuex data in asyncData but I can't. Also I can't seem to be able to use axios module in asyncData.
I tried a lot.
pages/index.vue
export default {
asyncData() {
//in my project there's a lot more code here but i think this is enough
let id = this.$store.state.id //i know i should prob use getter but no
//here i want to do an axios req with this var "id" but axios doesnt work
return axios.get(`url${id}`)
.then(...) //not gonna write it out here
}
}
store/index.js
export const state = () => ({
id: "#5nkI12_fSDAol/_Qa?f"
})
I expect it to get the ID from Vuex and then do a Axios req. Whole app doesn't work.

You can't use this in asyncData. The function asyncData gets passed a context object as param which basically represents the this keyword.
First you need to specify what do you want retrieve from the context object and then you can use it.
In your case do something like this:
export default {
asyncData({ $axios, store }) { //here we get Vuex and Axios
let id = store.state.id
return $axios.get(`url${id}`)
.then(...)
}
}

Related

Can't find the way to useSelector from redux-toolkit within event handler and pass params to it

There is an event handler for click and when it triggered i want to pull specific data from redux using selector where all logic many-to-many is implemented. I need to pass id to it in order to receive its individual data. Based on rules of the react the hooks can be called in function that is neither a React function component nor a custom React Hook function.
So what is the way to solve my problem ?
const handleMediaItemClick = (media: any): void => {
// For example i check media type and use this selector to pull redux data by id
const data = useSelector(playlistWithMediaSelector(imedia.id));
};
As stated in the error message, you cannot call hooks inside functions. You call a hook inside a functional component and use that value inside the function. The useSelector hook updates the variable each time the state changes and renders that component.
Also, when you get data with useSelector, you should write the reducer name you need from the redux state.
const CustomComponent = () => {
// The data will be updated on each state change and the component will be rendered
const data = useSelector((state) => state.REDUCER_NAME);
const handleMediaItemClick = () => {
console.log(data);
};
}
You can check this page for more information.https://react-redux.js.org/api/hooks#useselector
You should probably use local state value to track that.
const Component = () => {
const [imediaId, setImediaId] = useState(null);
const data = useSelector(playlistWithMediaSelector(imediaId));
function handleMediaClick(id) {
setImediaId(id)
}
useEffect(() => {
// do something on data
}, [imediaId, data])
return <div>...</div>
}
Does that help?
EDIT: I gather that what you want to do is to be able to call the selector where you need. Something like (considering the code above) data(id) in handleMediaClick. I'd bet you gotta return a curried function from useSelector, rather than value. Then you would call it. Alas, I haven't figured out how to that, if it's at all possible and whether it's an acceptable pattern or not.

Mock axios in a Vue application for frontend testing

When testing the frontend of my Vue application, I sometimes want to skip the communication with the API backend.
I came up with the following idea:
add state in Vuex store: skipServerCommunication.
when skipServerCommunication = false: send the axios request as expected
when skipServerCommunication = true: return mocked data
The easy way would be to add an check before every call of axios.post(...) or axios.get(...), but I don't want to have to add code in every api function I wrote.
So I thought about using the interceptors of axios, but I think it's not possible to stop a request, and just return mocked data, right?
I then thought about wrapping axios, and dynamically returning the real axios instance or a mocked one, which also implements the .post(), .get() and all the other axios functions based on the Store state. I thought about something like this (I'm working with TypeScript):
import store from '#/vuex-store'
const axios = Axios.create( // real store config );
const axiosMock = {
get(path: string) {
if (path == '/test') {
return Promise.resolve( // mocked data );
}
return Promise.resolve(true)
}
}
class AxiosWrapper {
get instance() {
if (store.state.skipServerCommunication) {
return axiosMock;
}
return axios
}
}
export default new AxiosWrapper();
But this solution has some problems:
I need to replace all axios calls with axiosWrapper.instance.get(...). Can I somehow avoid this and mock axios in a way that I can still work with axios.get(...)?
VSCode isn't able to provide autocompletion anymore because the returned instance is either of type AxiosStatic or "MyMockType". So I thought about implementing the AxiosStatic interface, but I struggle to do that correctly because of the two anonymous functions in the AxiosInstance interface. Is there an alternative way to overcome this problem?
Use axios-mock-adapter instead. You can mock axios calls as needed per test, using advanced matching patterns.
Example usage:
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
describe('UserList', () => {
it('gets users on mount', async () => {
const mock = new MockAdapter(axios)
mock.onGet('/users').reply(200, {
users: [{ id: 1, name: 'John Smith' }],
})
const wrapper = shallowMount(UserList)
await wrapper.find('.getUsersBtn').trigger('click')
expect(wrapper.vm.users[0].id).toBe(1)
})
})

How to rework an Angular1 App to a VueJs app

So I'll get right to it :
I'm trying to remake an existing app that used Angular 1 , instead using VueJS 2.
Being unfamiliar to Angular1 I find it challenging to decide on a few things :
1.What are factories(probably services), where do I place/write manage them?
2.Okay I know angular is big on controllers, but I cannot seem to understand if I were to use Vue , what's the alternative to the controller, and where to hold the code.
So, basically what I've gotten so far is to use VueX for state management and I have moved some services there, however - I can't figure out for example if a certain service #requires 'NameOfOtherService' , does it mean it imports it like in NodeJS const NameOfOtherService = require ('insert/path/here'); ?
Basically the app gets data from an API and php scripts, for example :
In the angular 1 version in my appsettings, which is using an AppConfig module I have a pathFromScript(script) => {} // etc.
My question is , how do I manage EVERYTHING that's going on within one app like that translated to Vue?
Thank you in advance I know it's entirely a shot in the dark here.
Yours truly ,
As for Vue.Js, your angular controllers are methods. If you need to get some data from an API, you can use a method to call a Vuex action and either return the value back to the method or add it to your state, so it's available everywhere. This will look like the example below.
I guess your factories / services are managing API calls. I would place that inside Vuex with axios to make API calls.
Methods is the same as controllers in Angular. You create methods on the specific page. If you need a method to be resused multiple places, you can use mixins. Mixins extends the specific page by importing it. You can read about it here: https://v2.vuejs.org/v2/guide/mixins.html
page.vue
{{data}}
export default {
import { mapActions, mapGetters } from 'vuex'
...
computed: {
...mapGetters({
data: 'getData'
})
},
methods: {
...mapActions({
getServerData: 'getDataFromServer'
})
},
created () {
this.getServerData()
}
}
store.js (vuex)
import axios from 'axios'
state: {
data: null
},
getters: {
getData: state => {
return state.data
}
},
mutations: {
setDataFromServer: (state, payload) => {
state.data = payload
}
},
actions: {
getDataFromServer: ({ commit }) => {
return axios.get('https://website.com/api/data').then((response) => {
commit('setDataFromServer', response)
})
}
}
In this example the created is calling the vuex action to get the data from the server. When the server returns the value, it calls a mutation which sets the response in a state (data). The mapGetters which calls the getters in vuex, returns the data from state. It updates whenever the state changes.
Hope this makes sense

Pattern for redirecting on unauthorized vuex actions

Navigation guards are perfect for redirecting unauthorized users to a login page, but what does one do to redirect unauthorized vuex actions to a login page?
I can do this easily enough in the vue method where I'm calling the action like so:
if (!this.isLoggedIn) {
this.$router.push({ name: 'login', query: { comeBack: true } })
$(`.modal`).modal('hide')
return
}
But then I'm inserting these 5 lines of code for every component method that requires authorization.
All the solutions I can think of sound hacky, so I'm wondering what the vuex way is:
In order to reject it at the vuex action level, I have to pass up the $router instance, and I'm still reusing the 5 lines for each action that requires auth.
I can handle this in a utility file, but then I'm handling $router instance in that file.
I can use a global vue mixin and call it (a) before making a call and then again (b) when getting a 401 back from the server.
All those seem odd. What vuex way am I missing here?
This sounds like a job for middleware. Unfortunately, Vuex doesn't have an official way to do middleware.
There is a subscribeAction() but that runs after the commit, so does not allow mods to the action. There is also a proposal Middleware processing between actions and mutation.
As I see it, we want middleware to be able to do two generic things
cancel the action
allow alternative actions to be called
The second is difficult to do without patching store.dispatch() or messing with the private property _actions after store has been created.
However, to guard an action as you describe, we only need to be able to cancel it.
Here is a poor-man's middleware for the modules pattern for Vuex store which I prefer.
store construction from modules
export const store = new Vuex.Store({
modules: {
config,
pages: applyMiddleware(pages),
measures,
user,
loadStatus,
search
}
})
applyMiddleware
const applyMiddleware = function(module) {
if(module.middlewares) {
Object.values(module.middlewares).forEach(middlewareFn => {
Object.keys(module.actions).forEach(actionName => {
const actionFn = module.actions[actionName]
module.actions[actionName] = addMiddleware(actionName, actionFn, middlewareFn)
});
})
}
return module;
}
addMiddleware
const addMiddleware = function(actionName, actionFn, middlewareFn) {
return function(context, payload) {
const resultFn = middlewareFn(actionFn)
if(resultFn) {
resultFn(context, payload)
}
}
}
defining middleware in the module
const actions = {
myAction: (context, payload) => {
...
context.commit('THE_ACTION', payload)
...
},
}
const middlewares = {
checkAuthMiddleware: (action) => {
return this.isLoggedIn
? action // if logged-in run this action
: null; // otherwise cancel it
}
}
export default {
state,
getters,
mutations,
actions,
middlewares
}
This implementation has module-specific middleware functions, but you could also define them globally and apply to as many modules as applicable.

How to chaining actions in Redux and React?

I am new to React, Redux and JS overall. I want to know how can I chain actions in a component? On a listing screen, my application retrieves user's Geo Position then it fetching api url with current long and lat.
I made it with time out. But I see it's a risky way.
import {
aFetch,
aGetPosition,
} from '../../actions';
import { API_URL } from '../../config/ApiEndpoints';
class App extends Component {
componentDidMount() {
this.props.aGetPosition();
setTimeout(() => {
const { latitude, longitude } = this.props.position.data.coords;
const url = `${API_URL}=${latitude},${longitude}`;
this.props.aFetch(url);
}, 1000);
}
render(){...}
}
const mapStateToProps = (state) => {
return {
position: state.position,
items: state.items,
};
};
export default connect(mapStateToProps, { aFetch, aGetPosition })(App);
Is possible to make it using .then() or something equal?
I believe you need the method componentWillReceiveProps(). The docs provide a good explanation about it and other methods in the lifecycle of a component.
Firstly, you'll want to dispatch your redux actions. This is possible by using a mapDispatchToProps function, which will be the 2nd parameter in your connect function (instead of a plain object with your actions, which is what you have now). Docs on implementing redux within your React containers, including dispatching actions from your container: http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components
To handle async actions and chaining actions, you'll likely want to use a middleware to help you, such as redux-thunk. Doc link: http://redux.js.org/docs/advanced/AsyncActions.html#async-action-creators (I recommend reading this whole page, but the link will go directly to the section for redux-thunk).

Categories