Vue Pinia access other function in getters with arrow functions - javascript

example store
getters: {
setName: (state) => (string: string) => {
state.user.username = string;
},
setUser: (state) => {
setName('Tom'); // setName is undefined.
}
}
Is there any way to access the setName inside an arrow function?
Example only show you can get this inside normal functions
https://pinia.vuejs.org/core-concepts/getters.html#accessing-other-getters

First of all, as Phil pointed out, getters feature should not mutate the state. As mutations are not included in Pinia, actions is the way to go.
Also, Arrow functions are meant to not have a this object reference. So, If you want to access any of the other store methods or variables in the state you should create an action with a regular javascript function syntax, like
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: (): myState => ({
user: {name: 'myname'},
}),
getters: {
getName: (state: myState) => state.user.name,
},
actions: {
setName(newName: string) {
this.user.name = newName
},
},
})
Then in any external file where you already imported the defined store, call the action just like this
import { useUserStore} from '~/stores/user'
const store = useUserStore()
store.setName('Tom')
Note: In Pinia actions you may want to always use regular javascript function syntax as those are in charge of changing data in your state.

Related

Resetting a vuex module's state to some initialState value

I'm attempting to come up with a method which can reset a modules state back to some initial value, and so far I am coming up o a few conceptual issues.
My base store is the following:
const store = new Vuex.Store({
modules: {
moduleA: moduleA,
},
actions: {
resetAllState: ({ dispatch }) => {"moduleA");
},
resetModuleState: (currentModule) => {
// Perform the reset here...somehow?
}
}
});
The moduleA.js store is created as follows with an intialState:
const initialState = () => ({
helloWorld: {}
});
const moduleA = {
namespaced: true,
state: {
initialState: initialState(),
...initialState(),
}
};
export default moduleA;
So I utilise a spread operator to create the following:
const moduleA = {
namespaced: true,
state: {
initialState: initialState(),
helloWord: {}
}
};
I can then mutate state.helloWorld whilst keeping a copy of the state.initialState...
So, my question now is, within the following global store
resetModuleState: (currentModule) => {
// Perform the reset here...somehow?
}
action, how do I actually perform the reset?
I have treid this as a global way of resetting state:
resetAllState: function() {
let defaultState = {};
Object.keys(store.state).forEach((key) => {
defaultState = {
...defaultState,
[key]: store.state[key].initialState ? store.state[key].initialState : store.state[key],
};
});
store.replaceState(Object.assign({}, defaultState));
},
But to no luck...?
The above code is having issue, the actions can have only access to
{ commit, dispatch, getters } as params
In above code "resetModuleState" takes modueleName in the first parameter, but it will have an object, so directly access the module object by below approach and reset the stathe by just calling initialState() function from each module you have
use this code inside the action "resetAllState"
resetAllState: function({ dispatch }) {
this._modules.root.forEachChild((childModule) => {
childModule.state.initialState();
});
},

Vue add reusable mutation to multiple modules

I have a mutation can be reused across multiple vuex modules but modifies the state at a module level. How can the mutation be separated out so that it can be dropped into each module's mutations without having to repeat the code?
const state = {
fieldInfo: {}
}
const actions = {
async getOptions({ commit }) {
commit('setOptions', await Vue.axios.options('/'))
}
}
const mutations = {
setOptions(state, value) {
// long mutation happens here
state.fieldInfo = value
}
}
export default {
namespaced: true,
state,
actions,
mutations
}
As you already have your stores namespaced this should work perfectly. All you need to do is move the mutation function to it's own file and then import it in the stores that you need it.
export default function (state, value) {
// long mutation happens here
state.fieldInfo = value
}
Then in your store
import setOptions from './setOptions.js'
const state = {
fieldInfo: {}
}
const actions = {
async getOptions({ commit }) {
commit('setOptions', await Vue.axios.options('/'))
}
}
const mutations = {
setOptions
}
export default {
namespaced: true,
state,
actions,
mutations
}
Someone will probably have a better answer. But in its current state 'fieldInfo' is a shared vuex property. It's like saying window.somVar and expecting to have someVar be a different instance depending on what module is using it. That's not really possible without declaring a new instance of a class, etc. At the most basic level you would need something more like fieldInfo{moduleA: val, moduleB: val}

Redux-Thunk getStore() doesn't retain state. Returning 'undefined'

This might be a question of best practices but I'd appreciate an explanation on why this doesn't work. I'm using Typescript + Redux + Thunk and trying to call actions like this:
export const requestUserDashboards = createAction<DashboardModel>(Type.REQUEST_USER_DASHBOARDS);
Dispatch in the fetch:
export const fetchDashboards = () => {
return async (dispatch: Dispatch, getState: any) => {
try {
dispatch(requestUserDashboards({
currentDashboard: getState.currentDashboard,
dashboards: getState.dashboards,
hasDashboards: false,
error: getState.error
}))
...
}
})
}
Here's the corresponding reducer:
export const dashboardReducer = handleActions<RootState.DashboardState, DashboardModel>(
{
[DashboardActions.Type.REQUEST_USER_DASHBOARDS]: (state = initialState, action): RootState.DashboardState => ({
currentDashboard: action.payload!.currentDashboard,
dashboards: action.payload!.dashboards,
hasDashboards: action.payload!.hasDashboards,
error: action.payload!.error
})
},
initialState
);
dispatch is working, however, getState doesn't correctly collect the current store state. I'm testing this by doing the following in the component receiving the updated store:
componentWillReceiveProps(nextProps: Login.Props) {
console.log(nextProps.defaultAccounts.defaultAccount);
}
Calling this in the component using:
this.props.defaultAccountActions.fetchUserDefaultAccount();
The action is working as the values from the fetch are being captured.
However, where I am using the getState.xxxx, these values are returning as undefined:
index.tsx:84 Uncaught TypeError: Cannot read property 'defaultAccount' of undefined
The initialState from my reducer is working. I can see this from doing the console.log(this.props.defaultAccounts.defaultAccount) from the componentWillMount() function.
I'm not sure what else I can provide. I think I'm actually just fundamentally misunderstanding how actions/reducers manage the store.
Questions
I am trying to get the current store values by using the getState.xxxx in the dispatch. Is this the correct way to do this?
isn't getState a function in that place? So you would need to do something
const state = getState();
and then use state inside dispatch
found in documentation, yeah it is a function at that place so you should firstly invoke a function to get state and then use it (e.g. from documentation below)
function incrementIfOdd() {
return (dispatch, getState) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
If you are using mapstatetoprops in your component you can use that to get the values from store. mapStateToProps first argument is actually the Redux state. It is practically an abstracted getState().
const mapStateToProps = function(state, ownProps) {
// state is equivalent to store.getState()
// you then get the slice of data you need from the redux store
// and pass it as props to your component
return {
someData: state.someData
}
}

this.props.dispatch().then is not a function

I am trying to do something as soon as a dispatch action finishes, and a similar question led me to try to use .then. I get an error that it doesn't exist, this.props.dispatch().then is not a function:
export function unpackStore(redux_store, namespace) {
// namespace is determined by what name you gave each reducer in combineReducers; client/reducers/index.js
let final_props = {};
let KEYS = Object.keys(redux_store[namespace]);
for (let key of KEYS) {
final_props[key] = redux_store[namespace][key];
}
return final_props;
}
export function basicUnpackStoreClosure(namespace) {
return function(store) {
let props = unpackStore(store, namespace);
return props;
}
}
#connect(basicUnpackStoreClosure('login_info'))
export default class LoginPage extends MyComponent {
constructor(props) {
let custom_methods = [
'handleLoginOrRegisterToggle',
'handleOnKeyDownInInputs',
'onLoginSubmit',
'onRegisterSubmit',
]
super(props, custom_methods);
this.state = {
mode: 'login',
email: '',
password: '',
password_confirm: ''
};
if (props.mode == 'register') {
this.state.mode = 'register';
}
}
onLoginSubmit() {
let self = this;
let reroute = function() {
browserHistory.push(self.props.destination_url);
}
this.props.dispatch({type: 'LOG_IN', payload: "fake#fake.com"})
.then((response) => {
browserHistory.push(self.props.destination_url);
})
}
We also tried the way you do something when this.setState finishes, passing it as a second arg:
this.props.dispatch({type: 'LOG_IN', payload: "fake#fake.com"}, reroute)
neither worked. The this.props.dispatch seems to come for free with using the #connect decorator. How can I run something only after this Redux store is updated with the "LOG_IN" action?
Normally the connect HOC is used to bind the actions with a component. For an example,
export default connect(mapStateToProps, mapDispatchToProps)(ApplicationContainer);
After you do this, then this.props.getApplications will be bound where getApplications is the action you need to fire.
As per your comment, if you want to have access to this.props.dispatch INSTEAD of binding actions, simply call connect() without passing any mappers, and the default behavior will inject dispatch.
You may install 'redux-promise' middleware and include it in your store. The problem here is that the promise is not getting fulfilled. Including 'redux-promise' middleware will solve the problem

ES2015 function definition with implicit argument

I am trying to translate the following from ES2015 to vanilla javascript:
fileA.js
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
fileB.js
import checkout;
checkout();
So far (using https://babeljs.io/repl/) I have:
fileA2015.js:
module.exports = {
checkout: function (_ref) {
dispatch = _ref.dispatch;
dispatch(types.CHECKOUT_REQUEST)
}
};
But I cannot figure out what to pass to checkout in fileB to have access to the correct _ref. What is this and where does it come from?
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
In the above function you are destructuring an object, which is expected to have a dispatch property. Where in dispatch is a callback function.
when you pass in the data object as a parameter, because of { dispatch }, which destructures the object that is passed in, it is expected to have dispatch property.
var data = {
dispatch: (type) => {
.......
}
};
So when checkout method is referenced and invoked in fileB.js, then you will have to pass in the data object at this point.
fileB.js
var data = {
dispatch: (type) => {
.......
}
};
import checkout;
checkout(data);
Let's note a few things here:
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
In the code above, checkout() is a function that expects a single parameter -- specifically an object with the property dispatch. dispatch is also expected to be a function.
If you don't pass any parameters to checkout() (or an object that doesn't have a dispatch property), dispatch will be undefined.
Therefore you need to pass something like:
checkout({ dispatch: function(){} })

Categories