Update data using vuex given items - javascript

I have created a module for reservations like this, this is my vuex store:
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = {
reservations: [],
stats: {
pending: 0,
confirmed: 0,
cancelled: 0
}
};
const actions = {
fetchReservations({commit}){
axios.get('/reservations').then(({data})=>{
commit('setReservations', data);
}).catch(error => {
throw new Error(error);
});
},
deleteReservation({commit}, reservationId){
axios.delete('/reservations/'+ reservationId).then(()=>{
commit('removerReservationInList', reservationId);
});
},
confirmReservation({commit}, reservationId){
axios.patch('/reservations/'+ reservationId +'/confirm').then(({data})=>{
commit('updateReservationInList', data);
});
},
cancelReservation({commit}, reservationId){
axios.patch('/reservations/'+ reservationId +'/cancel').then(({data})=>{
commit('updateReservationInList', data);
});
},
fetchReservationStats({commit}){
axios.get('/reservations/stats').then(({data})=>{
commit('setReservationsStats', data);
});
}
};
const mutations = {
setReservations(state, reservations) {
state.reservations = reservations;
},
removeReservationInList(state, reservationId){
state.reservations = state.reservations.filter((reservation)=>{
return reservation.id !== reservationId
});
},
updateReservationInList(state, data){
state.reservations = state.reservations.map(reservation => {
if (reservation.id !== data.id) {
return reservation;
}
reservation.state_id = data.state_id;
return reservation;
});
},
setReservationsStats(state, data) {
state.stats = data;
}
};
const getters = {
reservationsList(state){
return state.reservations
},
reservationsStats(state){
return state.stats;
}
};
export default new Vuex.Store({
state,
actions,
mutations,
getters
});
And those are the reservations:
[
{"id":1,"name":"Rollin Koss","email":"predovic.wyatt#example.net","state_id":2, "booking_date":"2020-12-12","number_of_guests":3},
{"id":2,"name":"Kellie Schroeder","email":"nicolette39#example.com","state_id":1,"booking_date":"2020-12-02","number_of_guests":14},
{"id":3,"name":"Autumn Goldner IV","email":"vkutch#example.org","state_id":3, "booking_date":"2020-12-15","number_of_guests":14}
.....
]
So, I get the stats in other request.
I was thinking doing it in another way, for example, when I get the reservations, return the stats like this:
[
"reservations": [
{"id":1,"name":"Rollin Koss","email":"predovic.wyatt#example.net","state_id":2, "booking_date":"2020-12-12","number_of_guests":3},
{"id":2,"name":"Kellie Schroeder","email":"nicolette39#example.com","state_id":1,"booking_date":"2020-12-02","number_of_guests":14},
{"id":3,"name":"Autumn Goldner IV","email":"vkutch#example.org","state_id":3, "booking_date":"2020-12-15","number_of_guests":14},
....
....
],
"stats": {
"pending": 50,
"confirmed": 30
"cancelled": 10
}
]
state_id = 1 is for pending reservations, state_id = 2 is for confirmed reservations, state_id = 3 is for cancelled reservations
And then for example, when I update a pending reservation to confirmed, the pending should decrease and the confirmed should increase, and if a confirmed reservation is cancelled, the stats should reflects that, also, if some reservation is deleted for example a pending, then it should decrease, I am not sure how to do it. Thank you.

Instead of storing stats as a state object, you could use a getter which will be reactive to your reservation state.
getters: {
stats(state){
return {
pending: countState(1),
confirmed: countState(2),
cancelled: countState(3),
};
function countState(stateId){
return state.reservations.reduce((acc, cur) => (cur.state_id === stateId ? ++acc : acc), 0);
}
}
EDIT: If you'd like it to reflect your current paginated reservations you could move the stats code from vuex into the component itself using a computed function, like so:
computed: {
stats: function () {
// This should point to your current set of paginated reservations
const reservations = this.$store.getters.reservations;
return {
pending: countState(1),
confirmed: countState(2),
cancelled: countState(3),
};
function countState(stateId) {
return reservations.reduce((acc, cur) => (cur.state_id === stateId ? ++acc : acc), 0);
}
},
},

Related

Vue, Vuex & remote storage

Well a have some value in remote storage (lets say x) and b-form-checkbox that should control this value. I want to inform user if value actually changed on storage and time when it happens.
So basically I want:
When user check/uncheck b-form-checkbox I want to change state of b-form-checkbox, send async request to the remote storage and show some b-spinner to indicate that state isn't actually changed yet.
When I receive answer from remote storage:
if change was successful just hide b-spinner.
if change was not successful (timeouted, error on server, etc) I want to change b-form-checkbox state back (since value actually doesn't changed on storage) and hide b-spinner
What is the silliest way to do int using Vue + Vuex?
Currently I'm doing it this way:
xChanger.vue:
<template>
<b-form-checkbox v-model="xComp" switch>
{{xComp ? 'On' : 'Off'}}
<b-spinner v-if="!xSynced"/>
</b-form-checkbox>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
export default {
name: 'XChanger',
computed: {
...mapState(['x']),
...mapGetters(['xSynced']),
xComp: {
get() { return x.local },
set(value) {
if (value != this.x.local) {
this.setX(value)
}
},
},
},
methods: {
...mapActions(['setX']),
},
}
</script>
main.js
import Vuex from 'vuex'
import Axios from 'axios'
const store = new Vuex.Store({
state: {
x: {
remote: null,
local: null
},
getters: {
xSynced(state) {
state.x.local === state.x.remote
}
},
actions: {
async setX(store, value) {
store.state.x.local = value
try {
let response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200) {
store.state.x.remote = value
}
} catch (error) {
store.state.x.local = !value
}
}
},
mutations: {
setX(state, value) {
state.x.local = value
state.x.remote = value
}
}
},
})
But it is too verbose for just one value to be controlled (especially computed property xComp). I'm sure that such a simple template should be already solved and has more simple way to implement.
Here is an example:
<template>
<b-form-checkbox v-model="x.local" switch>
{{x.local ? 'On' : 'Off'}}
<b-spinner v-if="saving"/>
</b-form-checkbox>
</template>
<script>
export default
{
name: 'XChanger',
data: () => ({
x:
{
local: false,
remote: false,
},
saving: false,
}),
watch:
{
'x.local'(newValue, oldValue)
{
if (newValue !== oldValue && newValue !== this.x.remote)
{
this.updateRemote(newValue);
}
}
}
methods:
{
async updateRemote(value)
{
try
{
this.saving = true;
const response = await Axios.post('http://blah.blah/setX', {x: value});
if (response.status == 200)
{
this.x.remote = value;
}
else
{
this.x.local = this.x.remote;
}
}
catch (error)
{
this.x.local = this.x.remote;
}
this.saving = false;
}
},
}
</script>

Lit-Element and Redux: Update Object in Array

I am trying to update the roll property in an object which is nested in the players array.
My state looks like this:
players: [
{
"id": "44ufazeep0o",
"name": "test-player-1",
"roll": 0,
"totalWins": 0
},
{
"id": "eu8hutex7z9",
"name": "test-player-2",
"roll": 0,
"totalWins": 0
}
]
This is my action:
export const addRoll = (roll, id) => {
return {
type: ADD_ROLL,
roll,
id,
}
}
This is my reducer:
case ADD_ROLL:
return state.players.map((player) => {
if (player.id === action.id) {
return {
...player,
roll: action.roll,
}
}
return player;
});
...
I am watching state.players in an components like so:
import { connect } from 'pwa-helpers'; // connects a Custom Element base class to the Redux store
...
stateChanged(state) {
this.players = state.players;
}
render() {
return html`${this.players.map((player)=> html`<h1>${player.name}</h1>`)}`
}
Now, whenever I call store.dispatch(addRoll(this.roll, this.id)), this.players.map() returns undefined in the component where I am watching state.players.
Any input on why this might happen is much appreciated!
You have return an array instead of an object with players key in it from state after ADD_ROLL action is dispatched. Correct way to update it would be
case ADD_ROLL:
return {
...state,
players: state.players.map((player) => {
if (player.id === action.id) {
return {
...player,
roll: action.roll,
}
}
return player;
});
}
...

Vuex getters has undefined data when I try to load data from API and use it in multiple components

I have a page component where I am making api call and storing the data in Vuex store through actions. This data has to be used at multiple places but everywhere I'm initially getting undefined data which loads after a few seconds asynchronously from the API. How should I use vuex getters asynchronously ?
Here's the code for my vuex store module :
import axios from 'axios';
const state = {
all_pokemon: {},
pokemon_details: {}
};
const getters = {
getAllPokemon: function(state) {
return state.all_pokemon;
},
getPokemonDetails: function(state) {
return state.pokemon_details;
}
};
const mutations = {
setAllPokemon: function(state, payload) {
return state.all_pokemon = payload;
},
setPokemon: function(state, payload) {
console.log('Pokemon details set with payload ', payload);
return state.pokemon_details = payload;
}
};
const actions = {
setPokemonAction: function({ commit }, passed_pokemon) {
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
});
commit('setAllPokemon', response.data);
},
setPokemonDetailAction: function({ commit }, passed_pokemon) {
console.log('Action method called..', passed_pokemon);
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
commit('setPokemon', response.data);
});
}
};
export default {
state,
getters,
mutations,
actions,
};
And code for the component where I want to get this data and pass it to other components :
<script>
import { mapGetters, mapActions } from 'vuex'
import axios from 'axios'
// Imported other components here
export default {
name: 'pokemon_detail_page',
data() {
return {
current_pokemon: this.$route.params.pokemon,
isLoading: false,
childDataLoaded: false,
search_pokemon: '',
sprites: [],
childData: 'False',
isAdded: false,
pokemon_added: 'none_display',
show: false
}
},
methods: {
...mapActions([
'setPokemonDetailAction',
'removePokemon'
]),
},
computed: {
...mapGetters([
'getPokemonDetails',
'getTeam'
])
},
components: {
Game_index,
PulseLoader,
PokemonComponent,
},
filters: {
},
watch: {
getTeam: function (val) {
},
getPokemonDetails: function(val) {
}
},
created() {
setTimeout(() => {
this.show = true;
}, 2000);
this.$store.dispatch('setPokemonDetailAction', this.current_pokemon)
.then(() => {
// const { abilities, name, order, species, } = {...this.getPokemonDetails};
})
},
mounted() {
},
}
</script>
And here's is the code for the template where I'm passing this data to multiple components :
<div v-if="show" class="pokemon_stats_container" :key="childData">
<ability-component
:abilities="getPokemonDetails.abilities"
>
</ability-component>
<sprites-component
:sprites="sprites"
>
</sprites-component>
<location-component
:location_area="getPokemonDetails.location_area_encounters"
:id="getPokemonDetails.id"
>
</location-component>
<stats-component
:stats="getPokemonDetails.stats"
>
</stats-component>
<game_index
:game_indices="getPokemonDetails.game_indices"
/>
<moves-component
:moves="getPokemonDetails.moves"
:pokemon_name="getPokemonDetails.name"
>
</moves-component>
</div>
As of now, I've adopted a roundabout way of doing this through setTimeout and setting a variable after 2 seconds so that data is available for other components to use. But, there has to be a more elegant way of handling vuex asynchronous data. Someone please help me in this.
Your first commit is not in the promise
commit('setAllPokemon', response.data);
make this :
axios.get('https://pokeapi.co/api/v2/pokemon/' + passed_pokemon)
.then((response) => {
console.log('Response data is : ', response.data);
commit('setAllPokemon', response.data);
});
try to use in your vue component
$forceUpdate()
when your request is end for reload the data

React Redux store changes sometimes rerender and sometimes dont

So i am using the fetch API with react / redux to perform API calls to Blockv in my actions and store a returned array of objects in my redux store using a reducer. My array of objects only contains one level of objects (i.e. {id: 9798234982739847, name: 220398402343, etc..}). My table that SHOULD rerender everytime the array is replaced - and I use replace as i want to completely replace the array in the store with the new - only rerenders sometimes and i can see the changes reflected in the store.
I am using thunk middleware. Here is what im working with:
store.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { createLogger } from 'redux-logger'
import urbanArcadeReducer from '../reducers/reducers'
import { loadTokens } from '../localStorage'
import { checkAuth } from '../actions/check-auth'
const persistedTokens = loadTokens();
const loggerMiddleware = createLogger();
// Store for Urban Arcade Application
const store = createStore(
urbanArcadeReducer,
persistedTokens,
applyMiddleware(
thunk, // lets us dispatch() functions
loggerMiddleware // neat middleware that logs actions
)
)
// Check for automatic login on page load
store.dispatch(checkAuth(store.getState().access_token))
export default store
reducers.js
function atoms(state = {
receivedAt: null,
atoms: []
}, action) {
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
atom = {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
return atom
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}
default:
return state
}
}
actions.js
import {
fetchRequest,
fetchFailure,
fetchSuccess,
updateNotifier,
updateCurrAtoms } from './action-creators'
import { bringToLogin } from './bring-to-login'
export const UPDATE_CURR_ATOMS = 'UPDATE_CURR_ATOMS'
export function updateCurrAtoms(atoms) {
return { type: UPDATE_CURR_ATOMS, atoms: atoms.atoms, receivedAt: atoms.receivedAt }
}
/**
* Submits request to get all arcade games using Blockv Discover Endpoint(vAtoms)
*
* #returns list of arcade cabinets (vAtoms)
*/
export function getAtoms(params) {
var access_token = params.access_token
var from_refresh = params.from_refresh
var responseCode = ''
var method = 'POST'
var url = 'https://api.blockv.io/v1/vatom/discover'
var headers = {
'Content-Type': 'application/json',
'App-Id': '<App ID>',
'Authorization': 'Bearer ' + access_token
}
var requestBody = {
"scope": {
"key": "vAtom::vAtomType.template",
"value": "<publisher_fqdn>"
},
"filters": [
{
"filter_elems": [
{
"field": "vAtom::vAtomType.template",
"filter_op": "Match",
"value": "<publisher_fqdn>"
}
]
}
],
"return": {
"type": "*",
}
}
var requestBodyJSON = JSON.stringify(requestBody)
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
// thus making it able to dispatch actions itself.
return function(dispatch) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
dispatch(fetchRequest())
console.log('Sending get atoms request to Blockv...');
fetch(url, {
method: method,
body: requestBodyJSON,
headers: headers
}).then(response => {
responseCode = response.status
console.log(responseCode)
return response.json()
}).then(data => {
if (responseCode === 401) {
dispatch(bringToLogin("We've logged you out. Please reauthenticate"))
dispatch(fetchFailure('Failed to get atoms'))
} else if (responseCode === 200) {
var atoms = data.payload.results
dispatch(fetchSuccess('Retrieved atoms!')) // Array of template variations
if (from_refresh) {
dispatch(updateNotifier({
isOpen: true,
message: 'Successfully retrieved games!'
}))
}
dispatch(updateCurrAtoms({
atoms: atoms,
receivedAt: Date.now()
}))
}
}).catch(err => {
console.log(err)
dispatch(fetchFailure('Failed to get atoms'))
});
}
}
MyComponent.js
...
class GameStatsModal extends Component {
getDataAtoms = () => {
return this.props.atoms
}
/**
* Props for each row in table
*
* #memberof GameStatsModal
*/
setTrProps = (state, rowInfo, column, instance) => {
return {
style: {
marginBottom: 15
}
}
}
render () {
return (
<OuterContainer>
<StatsContainer>
<InnerContainer>
<img
src={RefreshIcon}
alt="Refresh List"
id="refreshGames"
className='refreshButton'
onClick={
() => {
store.dispatch(getAtoms({
access_token: store.getState().access_token,
from_refresh: true
}))
}
}
/>
<ReactTable
data={this.getDataAtoms()}
className='-highlight -striped gamesTable'
noDataText="Click Refresh to Load Games"
columns={columnsAtoms}
defaultSorted={[{id: 'created', desc: true}]}
getTrProps={this.setTrProps}
getTdProps={this.setTdProps}
pageSize={this.getDataAtoms().length}
showPagination={false}
resizable={false}
/>
</InnerContainer>
</StatsContainer>
</OuterContainer>
)
}
}
const mapStateToProps = state => ({
atoms: state.atoms.atoms
})
export default withRouter(connect(mapStateToProps)(GameStatsModal))
Again i know the updates are being made to store, so my question is am i somehow mutating the previous state somewhere? If not is it possible that since i have multiple dispatch calls being executed in actions, could they be interfering with each other and/or rerendering? not sure where else to look.
Looking forward to any suggestions, thank you!
i think you are mutating the state directly
try this one
switch (action.type) {
case UPDATE_CURR_ATOMS:
var mappedAtoms = action.atoms.map(atom => {
// directly returning the new object
return {
id: atom.id,
gameTitle: atom.private.GameTitle,
highscore: atom.private.HighScore,
highscoreOwner: atom.private.HighScoreOwner,
locationName: atom.private.LocationName,
scoreHistory: atom.private.ScoreHistory,
unpublished: atom.unpublished,
author: atom['vAtom::vAtomType'].author,
description: atom['vAtom::vAtomType'].description,
dropped: atom['vAtom::vAtomType'].dropped,
lat: atom['vAtom::vAtomType'].geo_pos.coordinates[1],
lng: atom['vAtom::vAtomType'].geo_pos.coordinates[0],
owner: atom['vAtom::vAtomType'].owner,
template: atom['vAtom::vAtomType'].template,
template_variation: atom['vAtom::vAtomType'].template_variation,
title: atom['vAtom::vAtomType'].title,
when_created: atom.when_created,
when_modified: atom.when_modified
}
})
return {
...state,
receivedAt: action.receivedAt,
atoms: mappedAtoms
}

Vue.js: mutation for deleting a comment

I have been working on the feature of comment deleting and came across a question regarding a mutation for an action.
Here is my client:
delete_post_comment({post_id, comment_id} = {}) {
// DELETE /api/posts/:post_id/comments/:id
return this._delete_request({
path: document.apiBasicUrl + '/posts/' + post_id + '/comments/' + comment_id,
});
}
Here is my store:
import Client from '../client/client';
import ClientAlert from '../client/client_alert';
import S_Helper from '../helpers/store_helper';
const state = {
comment: {
id: 0,
body: '',
deleted: false,
},
comments: [],
};
const actions = {
deletePostComment({ params }) {
// DELETE /api/posts/:post_id/comments/:id
document.client
.delete_post_comment({ params })
.then(ca => {
S_Helper.cmt_data(ca, 'delete_comment', this);
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
};
delete_comment(context, id) {
context.comment = comment.map(comment => {
if (!!comment.id && comment.id === id) {
comment.deleted = true;
comment.body = '';
}
});
},
};
export default {
state,
actions,
mutations,
getters,
};
I am not quite sure if I wrote my mutation correctly. So far, when I am calling the action via on-click inside the component, nothing is happening.
Guessing you are using vuex the flow should be:
according to this flow, on the component template
#click="buttonAction(someParams)"
vm instance, methods object:
buttonAction(someParams) {
this.$store.dispatch('triggerActionMethod', { 'something_else': someParams })
}
vuex actions - Use actions for the logic, ajax call ecc.
triggerActionMethod: ({commit}, params) => {
commit('SOME_TRANSATION_NAME', params)
}
vuex mutations - Use mutation to make the changes into your state
'SOME_TRANSATION_NAME' (state, data) { state.SOME_ARG = data }

Categories

Resources