Vue JS/Vuex - Getter ID not updating with route param ID - javascript

I'm getting the ID of a user based on the ID I pass in my route params. When I first load the page and access one of the users, the getter displays the ID from the route param accordingly, however once I go back, and click on another user, the ID from the param does not match with the getter. Instead the getter shows the ID of the previously accessed user. Can anyone kindly suggest a solution for this?
setup() {
const store = vuexStore;
const adminId = router.currentRoute.params.adminId;
console.log("ID param:", adminId);
getSelectedAdmin();
const selectedAdmin = computed(() => store.getters.getSelectedAdmin);
console.log("getter Id:", selectedAdmin.value.id);
function getSelectedAdmin() {
return store.dispatch(GET_ADMIN_BY_ID, adminId)
}
return {
selectedAdmin,
}
}

You should fetch the user in the onMounted hook.
(I use the script setup, vue-router#next & vuex#next)
<script setup>
import { ref, onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
const store = useStore()
const route = useRoute()
const selectedAdmin = computed(() => store.getters['getSelectedAdmin'])
onMounted(async () => { await store.dispatch('GET_ADMIN_BY_ID', route.params.adminId) })
</script>
<template>
<div v-if="selectedAdmin">
{{ selectedAdmin.username }}
</div>
<div v-else>Loading ...</div>
</template>
In your store :
Set initial value for selectedAdmin to null
In the GET_ADMIN_BY_ID action, reset selectedAdmin to null before updating
const state = {
currentAdmin: null,
}
const mutations = {
SET_ADMIN: (state, payload) => state.currentAdmin = payload
}
const getters = {
getSelectedAdmin: (state) => state.currentAdmin
}
const actions = {
GET_ADMIN_BY_ID: ({ commit }, id) => {
commit('SET_ADMIN', null)
return axios.get(`admin/${id}`)
.then((response) => { commit('SET_ADMIN', response.data) })
.catch((err) => { commit('SET_ADMIN', null) })
}

Related

React - Redux How to filter the global state when redirecting to other component

I have a React app in which a global state is setted by using redux, in one component a Form is filled and based on the inputs I do an axios request to the same endpoint from where redux fetch the data then I want to redirect to another component and filter the state based on this new request. The problem is that when I am redirected the same state that redux defined is shown and no the updated one.
My redux logic is this:
actionsCreator productActions.js
import { fetchProductsStart, fetchProductsSuccess, fetchProductsFailure } from '../slices/productsSlice';
import { baseUrl } from '../../shared/baseUrl';
export const fetchProducts = () => async dispatch => {
try {
dispatch(fetchProductsStart());
const response = await fetch(baseUrl+"products");
const data = await response.json();
dispatch(fetchProductsSuccess(data));
} catch (error) {
dispatch(fetchProductsFailure(error));
}
};
the slice reducer is productsSlice.js
import { createSlice } from "#reduxjs/toolkit";
const productsSlice = createSlice({
name: "products",
initialState: {
products: [],
loading: false,
error: null
},
reducers: {
fetchProductsStart(state) {
state.loading = true;
state.error = null;
},
fetchProductsSuccess(state, action) {
state.products = action.payload;
state.loading = false;
},
fetchProductsFailure(state, action) {
state.error = action.payload;
state.loading = false;
}
}
});
export const { fetchProductsStart, fetchProductsSuccess, fetchProductsFailure } = productsSlice.actions;
export default productsSlice.reducer;
and my store configureStore.js
import { configureStore } from "#reduxjs/toolkit"
import productsReducer from "./slices/productsSlice"
export const store = configureStore({
reducer: {
products: productsReducer
}
})
the logic in the form that I mentioned above in the handleSubmit is:
handleSubmit(event){
event.preventDefault()
// redirect to the store component with the search criteria
// the search criteria will be passed as query parameters
var tipo = this.state.tipo
var marca = toTitleCase(this.state.marca)
var linea = this.state.linea
axios.get(baseUrl+'products' + '/?tipo=' + tipo + '&marca=' + marca + '&linea=' + linea )
.then((response) => {
console.log('response.data',response.data)
// here I am using the useNavigate hook to redirect and set the state
// with the response that is returned with the axios request
this.props.navigate("/store",{
state:{
products:response.data
}
});
})
.catch((error) => {
console.log(error)
})
}
How could I correctly update my state in order to filter this based on the inputs gotten from the form inputs? Is there a better way to do this I am kinda newbie with redux I can´t figure how to update the state.
EDIT: I forgot to mention that the component from where I am redirecting is a class component and I can´t change it to functional one
EDIT2: I just could change the logic so now the component from where I am redirecting is a functional Component

Access Methods of an Object stored in state with Zustand

I am having an issue accessing methods of an object retrieved from state. What would be the correct way of storing an object, along with all of its methods in state so that they may be accessed later.
Store (store.jsx)
import create from "zustand";
import { persist } from "zustand/middleware";
import { cloneDeep } from "lodash";
const initialState = {
user: undefined,
profile: undefined,
}
let store = (set) => ({
...initialState,
setUser: (user) => set((state) => ({ user: cloneDeep(user) })),
setProfile: (profile) => set((state) => ({ profile: profile })),
logout: () => {set(initialState)},
});
store = persist(store);
export const useUserStore = create(store);
Setting State (file2.jsx)
import { useUserStore } from "../../store";
const setUser = useUserStore((state) => state.setUser);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
//all properties and methods defined here
setUser(user);
return () => unsubscribe();
}, []);
Accessing stored state (file2.jsx)
import { useUserStore } from "../../../store/userStore";
const user = useUserStore((state) => state.user);
//usage, no methods defined at this point.
user.updateEmail() <--- method is not defined
Workaround / Correct Implementation??
Access currentUser in auth instance.

How can i access child component method in parent component using ref in Vue3?

I am trying to access the child method in the parent component using vue3 and ref method. But it returns an error.
Uncaught TypeError: addNewPaper.value?.savePaper is not a function
Below is my code. Please guide me where i am wrong.
Child component
<script setup lang="ts">
import { useWindowScroll } from '#vueuse/core'
import { Notyf } from 'notyf'
import { computed, defineProps, reactive, ref } from 'vue'
import { papers } from '/#src/firebase/connections'
const companySize = ref('')
const businessType = ref('')
const productToDemo = ref('')
const date = ref(new Date())
const { y } = useWindowScroll()
const isStuck = computed(() => {
return y.value > 30
})
const initialState = reactive({
subject: '',
paper: '',
marks: '',
})
const notyf = new Notyf()
const props = defineProps({
subjects: { required: true },
})
const savePaper = () => {
papers
.add(initialState)
.then(() => {
notyf.success('Paper saved successfully')
})
.catch((err) => {
notyf.error('Something went wrong')
})
}
</script>
Parent component
const addNewPaper = ref()
const successSave = () => {
addNewPaper.value?.savePaper()
notyf.success('Your paper has been successfully created!')
}
<template #content>
<FormAddNewTopical ref="addNewPaper" :subjects="_subjects"></FormAddNewTopical>
</template>
Any solution appreciated!
Public members are supposed to be defined with defineExpose with script setup syntax:
defineExpose({ savePaper })
Or with ctx.expose in setup function:
setup(props, ctx) {
...
ctx.expose({ savePaper })
...

React Component keeps re-rendering

In my page I am calling an action in my useEffect to populate my reducer. I have a component level state called page which is a parameter in the action. So every time the value of page changes I would like the action to be called again because obviously I intend to get the data from different pages.
Sadly I run into errors in the console telling me the component has reached its limit for times of re-rendering.
Here is the relevant code:
const Home = props => {
const [page, setPage] = useState(1);
useEffect(() => {
props.getPopularMovies(page);
}, [page])
My props.getPopularMovies function is coming from my mapDispatchToProps function which is being passed into connect()
Entire Home Page:
import React, { useEffect, useState } from 'react'
import { connect } from "react-redux";
// Actions
import { getPopularMovies } from "../actions/movies.action";
const Home = (props) => {
const [page, setPage] = useState(1);
useEffect(() => {
props.getPopularMovies(page);
}, [page])
return (
<div>
{props.movies && props.movies.length > 0 && props.movies.data.results.map(movie => (
<p key={movie.id}>{movie.title}</p>
))}
<button onClick={setPage(page + 1)}>Next Page</button>
</div>
)
}
const mapStateToProps = state => {
return {
movies: state.movies.movies
}
}
export default connect(mapStateToProps, {
getPopularMovies
})(Home)
Action File:
import axios from "axios";
import { GET_MOVIES_FAIL, GET_MOVIES_SUCCESS } from "../constants/movies.constants"
export const getPopularMovies = (page) => async (dispatch) => {
try {
const config = {
params: {
api_key: process.env.REACT_API_KEY,
page
}
};
const movies = await axios.get('/movie/popular', config);
dispatch({
type: GET_MOVIES_SUCCESS,
payload: movies
})
} catch (err) {
dispatch({
type: GET_MOVIES_FAIL,
payload: err
})
}
}
When you assign the method setPage as an event handler to the onClick event, you are invoking it instead of assigning it. So, instead of this:
<button onClick={setPage(page + 1)}>Next Page</button>
try this:
<button onClick={() => setPage(page + 1)}>Next Page</button>

Next.js dynamic routes with Firestore collection

I'm looking for a way to have a dynamic route that displays for every document in a Firestore collection using Server-side Rendering.
For example, a document called foo would exist at test.com/foo under the [doc] page component. Any time a document is added, it should be able to be accessed through its respective URL.
I've tried this method but I haven't been able to get it to work.
I've also tried implementing getServerSideProps but have not had much success, any pointers would be appreciated.
Code from the method above as follows:
under pages/api/[doc].js
export default (req, res) => {
db.collection("docs")
.doc(req.query.name)
.get()
.then((doc) => {
res.json(doc.data());
})
.catch((error) => {
res.json({ error });
});
};
under pages/[shoal].jsx
import { useRouter } from "next/router";
import useSWR from "swr";
const fetcher = async (...args) => {
const res = await fetch(...args);
return res.json();
};
function Doc() {
const router = useRouter();
const { name } = router.query;
const { data } = useSWR(`/api/${name}`, fetcher);
if (!data) {
return "Loading...";
}
return (
<div>
<p>Title: {data.title}</p>
</div>
);
}
export default Doc;
You can try using getServerSideProps:
export const getServerSideProps = async (ctx) => {
const doc = await db.collection("docs").doc(ctx.query.id).get()
const data = doc.data()
if (!data) return { notFound: true };
return { props: { data } };
};
function Doc({data}) {
const router = useRouter();
const { name } = router.query;
if (!data) {
return "Loading...";
}
return (
<div>
<p>Title: {data.title}</p>
</div>
);
}
export default Doc;
Simple solution.
const { data } = useSWR(api ? '/api/${name}' : null, fetcher);
Conditionally fetch the data if your variable is defined, if not, don't pass a URL string, better yet; you can conditionally consider the fetcher for usage also.
const { data } = useSWR(name ? '/api/${name}' : null, name ? fetcher : null);

Categories