redux state not changing - redux

I'm using a Net Core, React-Redux boiler-plate, and when I run the fetch api action, the reducer state does not change at all.
Here is my action
import axios from "axios";
import config from '../config';
const ROOT_URL = config[process.env.NODE_ENV].api;
export const FETCH_EVENTS = "FETCH_EVENTS";
export function fetchEvents() {
const url = ROOT_URL +"/Event/GetAllEvents";
const request = axios.get(url);
return {
type: FETCH_EVENTS,
payload: request
};
}
my index reducer:
import { combineReducers} from 'redux';
import { routerReducer } from 'react-router-redux';
import dataReducer from './dataReducer'
const reducers = {
events: dataReducer
};
const rootReducer = combineReducers({
...reducers,
routing: routerReducer
});
export default rootReducer;
and my reducer:
import { FETCH_EVENTS } from "../actions/ExtractActions";
export default function (state = [], action) {
switch (action.type) {
case FETCH_EVENTS:
console.log("inside reducer")
return [action.payload, ...state];
}
return state;
}
So I add this code in the Home component:
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchEvents }, dispatch);
}
function mapStateToProps(state) {
return {
events: state.events
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
but when I try to run the action and try to see if the reducer state has changed, I get on console log an empty array for "this.props.events". Even though if I am trying to store api data to the state, I even tried modifying the reducer method and simply returning a string, but this.props.events returns an empty array [] again. I am guessing my redux is not working but I don't know why. I've been debugging all night long
componentWillMount() {
this.props.fetchEvents()
console.log(this.props.events)
}

I found the error. For some reason I had to call this.props.events in the render() method and not componentwillmount.

axios.get() is an async function. That's why you couldn't see the updated state when you logged it right after fetching the events. I would recommend you to use the redux-devtools-extension for debugging. Hope this helps. Cheers!

Related

How to use `useRoute`/`useRouter` in a Pinia Store using Setup Store syntax in Vue3?

I've been trying to get my Pinia store up and running in Vue 3 and it all has been pretty effortless until I wanted to access some parameters in the url.
I have a store (simplified) like so:
import { defineStore } from 'pinia';
import { useRoute } from 'vue-router';
import { useLocalStorage } from '#vueuse/core';
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const uuid = ref(
useLocalStorage('uuid', route.params.id)
)
return { uuid };
})
Unfortunately, the route remains undefined as if useRoute() is not triggered properly. I've seen that you can add plugins to add the router instance to the pinia store on initialisation, but there's no way I can find to access that this instance in a Setup Store.
Any help would be greatly appreciated
route is not defined when the pinia is initiated.
You need to wait a bit.
One way to do this is to call the function when the component is loaded.
export const useUserStore = defineStore('user', () => {
const route = useRoute();
const id = ref('');
const setId = () => {
id.value = route.params.id as string; // don't need as string if you don't use TypeScript
};
return { id, setId };
});
<script setup lang="ts">
import { useUserStore } from '../stores/user';
const user = useUserStore();
user.setId(); // call the function from pinia, route.params works just fine
</script>
Link Demo

Vue Reactivity: Creating reactive data with provide and inject

My aim is to validate a users' credentials from vue frontend, fetch their data, store the data in a globally available variable and use them in any component. After some work, I'm able to use Vue's reactive() method with inject and provide. Here's my current code:
In store/index.js
import { reactive, inject } from 'vue'
// global states
export const stateSymbol = Symbol('state')
export const createState = () => reactive({
backendRoute: 'http://127.0.0.1:8000/',
currentUser: {},
logIn: userData => {
let user = createState.currentUser
user = userData
console.log(user)
}
})
export const useState = () => inject(stateSymbol)
Then in main.js
import { createApp } from 'vue'
import App from './App.vue'
// store modules
import { stateSymbol, createState } from './store'
// create app
const app = createApp(App)
app.provide(stateSymbol, createState())
Then in login/register component named Login.vue
import { useState } from "../store"
import axios from 'axios'
export default {
name: 'RegisterLogin',
setup() {
let state = useState()
axios.get(getUser, jwtConfig).then(response => {
userId = response.data.id
const currentUser = getUserProfile+userId // URL
console.log(currentUser)
const getCurrentUser = axios.get(currentUser, jwtConfig)
getCurrentUser.then(response => {
state.logIn(response.data)
console.log(state.currentUser)
})
})
}
}
console.log(user) in store/index.js logs the userData as expected. I believe with that, createState.currentUser should be mutated. The problem however is console.log(state.currentUser) logs an empty proxy object. Also, if I try accessing the state.currentUser from another component like Home.vue:
<template>
{{ state.currentUser }}
</template>
<script>
import { useState } from '../store/'
export default {
name: 'Index',
setup() {
return {
state: useState(),
}
},
}
</script>
...an empty object is always displayed even after the user logs in successfully. Is there anything I'm doing wrong? Thank you for your assistance (in advance).
I was looking through the code but couldn't find anywhere where the variable was getting mutated, but I believe I know what's going on.
You are reassigning the user object, not assigning the createState.currentUser to another object.
import { reactive, inject } from 'vue'
// global states
export const stateSymbol = Symbol('state')
export const createState = reactive({
backendRoute: 'http://127.0.0.1:8000/',
currentUser: {},
logIn: userData => {
// over here you assign user to createState.currentUser
let user = createState.currentUser;
// then you reassign user to `user data`
user = userData
console.log(user)
}
})
export const useState = () => inject(stateSymbol)
you could instead try
import { reactive, inject } from 'vue'
// global states
export const stateSymbol = Symbol('state')
export const createState = () => reactive({
backendRoute: 'http://127.0.0.1:8000/',
currentUser: {},
logIn: userData => {
createState.currentUser = userData
console.log(createState.currentUser)
}
})
export const useState = () => inject(stateSymbol)
As an aside, why are you using inject? I can't figure out what you're expecting it to do.
export const useState = () => stateSymbol would have the same result, but you don't even need to pass the function, you can just use something more concise like...
//////// store
import { reactive } from 'vue'
// global states
export const store = reactive({
backendRoute: 'http://127.0.0.1:8000/',
currentUser: {}
})
export const logIn = (userData) => {
store.currentUser = userData
}
////////////////////////////////////////////////
////// main
import { store, logIn } from "../store"
import axios from 'axios'
export default {
name: 'RegisterLogin',
setup() {
axios.get(getUser, jwtConfig).then(response => {
userId = response.data.id
const currentUser = getUserProfile+userId // URL
console.log(currentUser)
const getCurrentUser = axios.get(currentUser, jwtConfig)
getCurrentUser.then(response => {
logIn(response.data)
console.log(store.currentUser)
})
})
}
}
It appears #Daniel was right after all. My logIn method in store/index.js was not really mutating the currentUser property. In fact, I'm unable to access that property from the method. To solve this, I had to do the mutation immediately after the axios call like state.currentUser = response.data in then method and boom, the state is updated in all components! Thanks.

How to fix "dispatch is not a function" error in react native project

In a react-native, redux, firebase project, I have a drawer component that subscribes to an onSnapshot listener when the component mounts, and on will unmount, it calls the snapshot reference. this component looks like this:
import { onAccountChange } from '../actions/Agenda';
import {dispatch} from 'redux';
class DrawerContentComponent extends React.Component {
constructor (props) {
super(props);
}
componentDidMount(){
this.unsubscribeAccount = firebase.firestore().collection('users').doc(this.props.authUser.uid).onSnapshot((doc) => {
dispatch({type: types.LOAD_ACCOUNT, payload: doc.data()})
});
}
componentWillUnmount() {
this.unsubscribeAccount();
}
< ...rest of component... >
EDIT:
const mapStateToProps = ({ account, auth, inbox, agenda }) => {
const { role, profileImg, legalName, username, rating, phoneNumber } = account;
const { conversations } = inbox;
const { authUser } = auth;
const { events } = agenda;
return {
role,
profileImg,
legalName,
username,
rating,
phoneNumber,
authUser,
conversations,
events
};
};
const mapDispatchToProps = { logoutUser, onProfileChange, onAccountChange, getConversations, getAgenda };
export default connect(mapStateToProps, mapDispatchToProps)(DrawerContentComponent);
}
Edit: onAccountChange():
export const onAccountChange = (uid) => {
return (dispatch) => {
firebase.firestore().collection('users').doc(uid).onSnapshot((doc) => {
dispatch({ type: types.LOAD_ACCOUNT, payload: doc.data() });
});
};
};
The above functions as necessary, because I couldn't manage to unsubscribe from the action, which previously was placed in an external directory for actions.
Problem: I want to be able to implement this by somehow using the function thats already created in the actions file ( getAgenda()) without having to rewrite the code in the component, because im currently doing that just to have the ability to unsubscribe from the listener on unmount, only way I thought of to make it work.
ideally, id like to do something like this:
componentDidMount() {
this.unsubscribeAgenda = this.props.getAgenda();
}
componentWillUnmount() {
this.unsubscribeAgenda();
}
But the above results in:
TypeError: 'dispatch is not a function' if I take out the dispatch import, the error is ReferenceError: Cant find variable: dispatch, I obviously need to dispatch changes for a onSnapshot listener
What are some strategies to handle this?
You can't import dispatch directly from redux.
You need to either use react-redux's connect() function to wrap your action creators with dispatch or get dispatch directly from it.
If you are using a functional component, you could use useDispatch to get access to it.
If you don't want to use one of the normal react-redux options, you can export dispatch from your store, and then import it from where you created your store.
export const dispatch = store.dispatch
If most of your logic for the firestore is in an redux thunk action (or similar with asynchronous capabilities), use connect to get the action wrapped in dispatch and run it as you have in your ideal at the end. Whatever you return from a thunk action is returned from the call as well, so you should be able to set it up to return the unsubscribe function.
connect({},{onAccountChange})(DrawerContentComponent)
Then you can dispatch onAccountChange action creator using:
this.props.onAccountChange()
Edit:
Modify your onAccountChange function to this so that your thunk returns your unsubscibe function.
export const onAccountChange = (uid) => {
return (dispatch) => {
return firebase
.firestore()
.collection('users')
.doc(uid)
.onSnapshot((doc) => {
dispatch({ type: types.LOAD_ACCOUNT, payload: doc.data() });
});
};
};
Then you just need to add onAccountChange to the mapDispatch to props and use this in your componentDidMount method:
this.unsubscribeAccount = this.props.onAccountChange();
For making components to be attached to store for both dispatch actions or mapping props, it is used with connect(mapStateToProps, mapDispatchToProps)(Component). in your case, there is no props passed to component so I'll just send null for mapStateToProps
(assuming you used Provider at some parent component REDUX. I cant understand how to connect a component defined as a class extending React.Component in order to read the store)
import { connect } from 'react-redux';
class DrawerContentComponent extends React.Component {
...rest code...
componentDidMount() {
this.unsubscribeAgenda = this.props.getAgenda();
}
componentWillUnmount() {
this.unsubscribeAgenda();
}
}
export default connect(null, { getAgenda })(DrawerContentComponent)

React-Redux unable to fetch data from firebase react native

I have created a database in firebase the schema is below:
Now All I have been trying to do is just have it show up when I do a console log but nothing shows up.
Below is the code for my JobsActions.js
import firebase from 'firebase';
import {
JOBS_FETCH_SUCCESS
} from './types';
export const jobsFetch = () => {
return (dispatch) => {
firebase.database().ref('/jobs')
.on('value', snapshot => {
dispatch({ type: JOBS_FETCH_SUCCESS, payload: snapshot.val() });
});
};
};
This is my reducer:
import {
JOBS_FETCH_SUCCESS
} from '../actions/types';
const INITIAL_STATE = {
// jobs: 'RCCA'
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case JOBS_FETCH_SUCCESS:
console.log(action);
return state;
//return action.payload;
default:
return state;
}
};
This is the JobsList
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text } from 'react-native';
import { jobsFetch } from '../actions';
class JobsList extends Component {
componentWillMount() {
this.props.jobsFetch();
}
render() {
return (
<View style={{ paddingTop: 20 }}>
<Text>Hello</Text>
</View>
);
}
}
export default connect(null, { jobsFetch })(JobsList);
I have authentication using firebase and its not a connection to firebase thats an issue, From what I see, it seems like maybe the ref path is wrong in the Actions file?
You main problem here is not with Firebase actually, since I believe everything else is allright, but with React-Redux.
When you are connecting a component to the store, the connect function recieves two functions. The first one (usually called mapStateToProps) recieves the state and returns an object that will be added to the props. In this case, you are not using it, so passing null is a valid decision.
The second one (usually called mapDispatchToProps) receives the dispatch as a parameter and should return an object with the functions that will be inserted to the props that can be used to dispatch new actions. In this case, you are just passing an object as the second parameter of the connect { jobsFetch }.
When you do this.props.jobsFetch(); you are actually returning the function that receives the dispatch, so nothing is actually executed.
Your mapDispatchToProps should be something similar to this
const mapDispatchToProps = dispatch => {
return {
jobsFetch : () => dispatch(jobsFetch())
}
}
export default connect(
null,
mapDispatchToProps
)(JobsList)
Here, I'm assuming that you are in fact using Redux thunk since you are returning a function that receives the dispatch as a parameter in your actions.
As you may see, we first call the jobsFetch() in order to get the function that receives the reducer, and then we dispatch it.
Let me know if this does not work! There may be something else that is not correct, but this is something that should be addressed. Hope it helps!

Reducer can't receive data from action creators

I've got a problem with my reducer. I have created an action creator, reducer, and used 'react-redux' connect to combine both.
When I'm fireing my action, action creator logs, that he just received a new data, but reducer does not log anything (Reducer logs only 3 initial loops). Also, store.getState() which I'm display in console every 5 seconds shows null (which is initial state of my store). Could you help me to deal with the problem?
export const UPDATE_PACKAGE_JSON = 'UPDATE_PACKAGE_JSON';
export function setName(name){
console.log('Action creator just received a name', name);
return {
type: UPDATE_PACKAGE_JSON,
payload: name
}
}
Container
const mapDispatchToProps = (dispatch) =>
bindActionCreators({ setName }, dispatch);
export default connect(null, mapDispatchToProps)(ConfigurationForm) ;
Reducer
import { UPDATE_PACKAGE_JSON } from './../actions/index';
export const packageJson = function (state = null, action){
console.log(UPDATE_PACKAGE_JSON);
switch(action.type){
case UPDATE_PACKAGE_JSON:
return {...state,
name: action.payload};
}
return state;
};
EDIT
Store
import { createStore } from 'redux';
import rootReducer from './../reducers/rootReducer';
const store = createStore(rootReducer);
export default store;
rootReducer
import { combineReducers } from 'redux';
import { packageJson } from './packageJson';
const rootReducer = combineReducers({
packageJson
});
export default rootReducer;

Resources