I have an action in my vuex store like this:
[FETCH_ADAM_BROWN_LIST](state)({commit}) {
/* Action logic */
},
I want to access similarly name actions, e.g. FETCH_CHRIS_MATHISON_LIST using a variable like so:
this.$store.dispatch(`FETCH_${this.person}_LIST`);
However this throws an error:
VM90646:37 [vuex] unknown action type: FETCH_ADAM_BROWN_LIST
But it will work when I specify it a constant:
this.$store.dispatch(FETCH_ADAM_BROWN_LIST);
How can I access 'constant' named actions with variables?
Have you try using mapActions? That could work for what you want. Helpful link: How to configure Vue mapActions
I use this structure in my Vuex Store. Many of mine actions are dispatched using a concatenated string as argument. But my actions are declared like this:
setCurrentUser: function ({ dispatch, commit, getters, rootGetters }, userDB) {
commit('setCurrentUser', userDB)
dispatch('resetDashboards', userDB.dashboards)
}
Then, I can dispatch like this:
let type = 'User'
this.$store.dispatch('setCurrent' + type)
Is there something strange in your code. Remove the (state), cause actions receive a context object which in your case, you are destructuring in the params. ({commit}).
Set like this and let me know if this works.
[FETCH_ADAM_BROWN_LIST]({commit}) {
/* Action logic */
},
As you have observed, when you passed FETCH_${this.person}_LIST to dispatch, the action type that was dispatched is the concatenated constant name and not the actual value of the constant variable.
To get the actual value of the your constant variable, you can use the eval function.
From the MDN docs:
The argument of the eval() function is a string. If the string
represents an expression, eval() evaluates the expression.
You can use it like this:
const FETCH_ADAM_BROWN_LIST = 'customAction';
const person = 'ADAM_BROWN';
const actionToDispatch = `FETCH_${person}_LIST`;
console.log(eval(actionToDispatch)); // customAction
Be careful with using eval though. If you're absolutely sure your input is sanitized before passing it along, then it shouldn't be a problem.
Related
I'm curious about passing props into setup and what are best practices to update variables/templates based on property changes.
I'm curious about reactive and computed.
For example:
setup(props) {
// Setup global config settings
const config = computed(() => {
return {
// See if the component is disabled
isDisabled: props.disabled, // (1)
// Test for rounded
isRounded: props.rounded // (2)
}
})
return { config }
}
Should config.isDisabled and config.isRounded be wrapped in their own computed function as they are both different and independent? However, it is easy to just stick them into one big function. What is best practice in this regard?
Does the entire config function evaluate once a single property changes within the function or can it recognize the change and update what is required?
Per docs, reactive is deeply reactive and used for objects, however, I've noticed it doesn't update to property changes. Therefore, I've been treating it more like data in Vue 2. Am I missing something or is this correct treatment?
You do not have to wrap props with computed at all, as they should be already reactive and immutable.
You also do not have to return config from your setup function as all props passed to your component should be automatically exposed to your template.
The computed function is evaluated only once and then Vue3 uses Proxy to observe changes to values and update only what's required. If you need to run a function every time a property changes you can use watchEffect.
Vue3 reactive is actually deep and works fine on objects. It should track all changes, unless you are trying to change the original object (the target of reactive function).
I have scoured the web and various sources; none seem to apply to my question. The closest might be this (which doesn't have an answer):
React + Redux function call
So: I am attempting to pass arguments along to one of my action creator fields, a function called update which will determine if the blurred row had a value changed, and if so it will call my api to update. The arguments I wish to pass are the event (which contains the row I need as target.ParentElement) and an integer that represents the index of the row in my state's projects property.
Action creator in my redux store:
export const actionCreators = {
update: (e: React.FocusEvent<HTMLInputElement> | undefined, i: number): AppThunkAction<KnownAction> => (dispatch, getState) => {
let test = event;
// Will put logic and api call in here and dispatch the proper action type
}
}
And trying to call it like so:
// Inside a function rendering each row in my form
...
<input key={project.number} name={name} className='trackerCell' onBlur={(event) => { this.props.update(event, i) }} defaultValue={project.number}/>
Where i is the index value, passed to the rendering function.
This all compiles find, however when I execute and get into the update function, e and i are both undefined; event is defined though, and looks as I would expect e to look.
FWIW, the format I'm attempting here works elsewhere in my application:
requestProjects: (programNumber: number, programString: string): AppThunkAction<KnownAction> => (dispatch, getState) => {
when called by componentWillUpdate() properly receives a number and string that I am able to use in my logic.
Bonus: In all my action creator functions constructed this way, arguments has 3 objects in it: dispatch, getState and undefined. Why don't the arguments in the call signature show up? Am I thinking about these arguments differently?
And yes, I know I can just attach the index value to an attribute in my input and that will appear in the event object, but this seems hacky, and I want to actually understand what is going on here.
Thanks
UPDATE
In response to Will Cain's comment: The index variable, i, is passed to the row rendering function from it's parent, as such:
private renderProjectRow(project: ProjectTrackerState.Project, i: number) {
let cells: JSX.Element[] = [];
let someKey = project.number + '_started', name = project.number + '_number';
cells.push(<input key={project.number} name={name} className='trackerCell' onBlur={ this._handleBlur.bind(this) } defaultValue={project.number}/>);
// Rendering continues down here
It's a valid number type up to the event point (I can tell as I debug in the browser).
The event variable in the update function comes from.. I don't know where? That's a mystery I would love to solve. Even though it is not a defined parameter to the update function, when I enter the update method, event is defined as such in the debugger:
Event {isTrusted: false, type: "react-blur", target: react, currentTarget: react, eventPhase: 2, …}
It is clearly the event that triggered the handler, but how it reaches the update function is beyond me.
Answering this in case anyone comes across this same issue (unlikely, but want to be helpful):
The reason my arguments e and i were undefined at runtime is because they were not referenced in the function execution. My guess (still looking for documentation to verify) is that typescript cleans up references to unused parameters. Makes sense from an optimization standpoint. Adding a read reference to e and i inside the update function solved my issue.
You can add "noUnusedParameters": true to your compilerOptions in your tsconfig file and this will throw a typescript error for all these parameters so you can catch when these cleanups would be done.
I am coming across this snippet of code in the redux-connect library and I wonder is it possible to put a function as a key in the computed key in ES6. How does this work and how javascript interprets ?
export const reducer = handleActions({
[beginGlobalLoad]: state => ({
...state,
loaded: false,
}),
[endGlobalLoad]: state => ({
...state,
loaded: true,
})
....
}
with beginGlobalLoad is a function that is created by
export const beginGlobalLoad = createAction('#redux-conn/BEGIN_GLOBAL_LOAD');
I read over the concept of dynamic computed key but it does not say anything about using the function as the key for the property.
Thanks a lot for your answer
Links to the library: https://github.com/makeomatic/redux-connect/blob/master/modules/store.js
No. Property keys must be either strings or symbols. If you use a function, it will get stringified like any other object, and that's usually not what you want.
However, the createAction docs specifically state that
createAction also returns its type when used as type in handleAction or handleActions.
which they achieve by overwriting toString.
I am trying to implement a graphQL API, it went well with queries but it's going not that well with mutations:
Here is my basic mutation using apollo-client and graphql-tag:
import gql from 'graphql-tag'
const addNewPlace = (place) => {
return client.mutate({
mutation: gql`
mutation {
addNewPlace(
input: {
$title: String!
}
) {
place { title }
}
}
`,
variables: {
title: place.title
}
})
}
Here I was trying to use variables. When changing the mutation to look like that one below, it is going smoothly however, it's not the right way to do id.
const addNewPlace = (place) => {
return client.mutate({
mutation: gql`
mutation {
addNewPlace(
input: {
title: "${place.title}"
}
) {
place { title }
}
}
`
})
}
Any idea where I made my mistake?
When using variables, there's three steps you need to take:
Add the variables to the request being sent. In Apollo, it's done by specifying a variables property inside the object you pass to mutate. You wrote:
variables: { title: place.title }
This is fine. We are sending some variable, called title, with whatever value, along with the request to our server. At this point, GraphQL doesn't even know about the variables.
Declare your variables inside your operation. You don't have to name your operation, but it's good practice to do so:
mutation AddNewPlace($title: String) {
Here, we are telling GraphQL we've included a variable called title. You could call it anything (foo for example), as long as it matched what you passed to the variables prop in #1. This step is important because A) GraphQL needs to know about the variable and B) it needs to know what type of variable you are passing in.
Finally, include the variable in your mutation, like this:
addNewPlace(input: { title: $title }) {
Be careful not to mix up your variable definition in step #2 with your input definition in step #3. Also, I'm assuming your typeDefs include some kind of input type like AddNewPlaceInput. Rather than passing in just title, you can pass in an object like this:
variables: { input: { title: place.title } }
Then your mutation looks like this:
mutation AddNewPlace($input: AddNewPlaceInput) {
addNewPlace(input: $input) {
# fields returned by the mutation
I would highly recommend enabling a GraphiQL endpoint so you can easily test your queries and mutations before implementing them on the client side.
Lastly, you may want to check and make sure the fields you are asking for in the mutation match your type definition. I'm just guessing here, but if your mutation resolves to a Place type, you wouldn't need to put place { title }, just title, unless your Place type actually has a place field.
Let's assume I have an action that gets dispatched on page load, like say, in the index.js file.
example
store.dispatch(loadData());
in my reducer I initialize state to to an object. Something like this
function myReducer(state = {}, action)
now I have some smart component that subscribes to my state and then passes it down to another component to display the data. Another important note for this scenario is the fact that the retrieval of the data is happening asynchronously.
Let's also assume that the key of this object is some array.
So the markup component would have something like this
{this.props.object.key.map(k => do something)}
Now since key is undefined, if I call map on it, I blow up. The way I have been dealing with this, is by using a simple if check. If key is defined then run .map otherwise return null. Then by the time my data gets back from the server, the render will be called again due to a change in state that this component subscribed to. At this point the key is defined and map can be called.
Another approach, Is to define what your state will look like in the reducer. In other words, if I know that my state will be an object with an array property on it, I might do something like this.
const initialState = {
key:[]
}
function myReducer(state = initialState, action)
Doing this will benefit in the fact that now I won't need my if check since key is never undefined.
My questions is; are any of these approaches better than the other? Or perhaps, is there another way entirely to deal with this?
Generally, the approach I like to take is to define default props on the component which have the semantic meaning of "empty." For example, in context of the issue you describe I would typically structure my component like this (ES6 classes style):
class MyComponent extends React.Component {
static defaultProps = {
object: {
key: []
}
};
...
method() {
// this.props.object.key is an empty array, but is overriden
// with a value later after completion of the reducer
// (trigerred asynchronously)
this.props.object.key.map(doSomething);
}
}
This is relatively clean, prevents the code from throwing at run time, and forces you to create well-defined behaviors for semantically null, empty, or undefined input states.