I currently started learning redux. My code was working perfectly with core redux, then I tried out #reduxjs/toolkit and now I'm unable to access the function to change the state in the store. Here is my code of reducer.
const seasonEdits = createSlice({
name: "seasons",
initialState: [],
reducers: {
addSeason(state, action) {
state.push(action.payload);
console.log("this here");
},
removeSeason(state, action) {
state.filter((season) => season.id !== action.payload);
},
markComplete(state, action) {
state.map((season) => {
if (season.id == action.payload) season.isWatched = !season.isWatched;
});
},
},
});
export const { addSeason, removeSeason, markComplete } = seasonEdits.actions;
export default seasonEdits.reducer;
and my store.js file
import { configureStore } from "#reduxjs/toolkit";
import seasonReducer from "./reducer";
export default store = configureStore({
reducer: {
seasons: seasonReducer,
},
});
and the add.js file which has add functionality. Calling a handleSubmit function which is creating an object and adding it to an array which is the state in store.
const handleSubmit = async () => {
try {
if (!name || !totalNoSeason) {
return alert("Please add both fields");
}
const seasonToAdd = {
id: shortid.generate(),
name,
totalNoSeason,
isWatched: false,
};
addSeason(seasonToAdd);
navigation.navigate("Home");
} catch (error) {
console.log(error);
}
};
const mapDispatchToProps = (dispatch) => {
return {
addSeason: (data) => dispatch(addSeason(data)),
};
};
Add.propTypes = {
addSeason: propTypes.func.isRequired,
};
export default connect(null, mapDispatchToProps)(Add);
The issue is that array.map() and array.filter() return new arrays! Right now your reducers are calling those functions, and then just throwing away the new arrays:
removeSeason(state, action) {
// The return value is thrown away and ignored!
state.filter((season) => season.id !== action.payload);
},
You need to return the new value:
removeSeason(state, action) {
// Now RTK will see the new return value
return state.filter((season) => season.id !== action.payload);
},
See https://redux-toolkit.js.org/usage/immer-reducers#resetting-and-replacing-state for more details.
Related
i am new to reactnative, i was trying to build todo app using useReducer and asyncstorage but not able to achieve that, i have read every article and related questions i am able to do it using useState but did not got the result with useReducer.
Any help/suggestions will be appreciated. Thank You .
const MainScreen = () => {
const getData = async () => {
try {
const data = await AsyncStorage.getItem('itemlist');
return (output = JSON.parse(data));
} catch (e) {
}
};
React.useEffect(() => {
getData();
}, []);
const [text, setText] = React.useState('');// for textinput
const { dispatch } = useContext(NotesContext);
const handleSubmit = async () => {
try {
const output = JSON.stringify(state);
await AsyncStorage.setItem('itemlist', output);
} catch (error) {
console.log(error);
}
if (text) {
dispatch({ type: 'ADD_TODO', payload: text });
}
setText('');
};
//this is my reducer function below
export const TodoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return state.concat({
task: action.payload,
id: uuid.v4(),
complete: false,
});
case 'DELETE_TODO':
return state.filter((todo) => todo.id !== action.payload);
default:
throw new Error();
}
};
I am new to vue.js, I have a simple web application(Vue frontend connected to a FastAPI backend) that a user can create an account and login, All of this works so far but when I refresh the page the user is logged out.
And console show an error:
Uncaught (in promise) TypeError: Cannot read property '$store' of undefined
What am I doing wrong? How to keep user logged in even after page refresh. Can anyone please help me?? thanks
store/index.js
import Vuex from 'vuex';
import Vue from 'vue';
import createPersistedState from "vuex-persistedstate";
import auth from './modules/auth';
// Load Vuex
Vue.use(Vuex);
// Create store
const store = new Vuex.Store({
modules: {
auth
},
plugins: [createPersistedState()]
});
export default store
store/modules/auth.js
import { postUserLogInAPI } from "../../service/apis.js";
const state = {
token: "",
expiration: Date.now(),
username: ""
};
const getters = {
getToken: (state) => state.token,
getUsername: (state) => state.username,
getFullname: (state) => state.fullname,
isAuthenticated: (state) => state.token.length > 0 && state.expiration > Date.now()
};
const actions = {
async LogIn({ commit }, model) {
await postUserLogInAPI(model).then(function (response) {
if (response.status == 200) {
commit("LogIn", response.data)
}
})
},
async LogOut({ commit }) {
commit('LogOut')
}
};
const mutations = {
LogIn(state, data) {
state.username = data.username
state.fullname = data.fullname
state.token = data.token
state.expiration = new Date(data.expiration)
},
LogOut(state) {
state.username = ""
state.fullname = ""
state.token = ""
state.expiration = Date.now();
},
};
export default {
state,
getters,
actions,
mutations
};
service/http.js
import axios from 'axios'
import { Loading, Message } from 'element-ui'
import router from '../router/index.js'
import store from '../store';
let loading
function startLoading() {
loading = Loading.service({
lock: true,
text: 'Loading....',
background: 'rgba(0, 0, 0, 0.7)'
})
}
function endLoading() {
loading.close()
}
axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://0.0.0.0:80/';
axios.interceptors.request.use(
(confing) => {
startLoading()
if (store.getters.isAuthenticated) {
confing.headers.Authorization = "Bearer " + store.getters.getToken
}
return confing
},
(error) => {
return Promise.reject(error)
}
)
axios.interceptors.response.use(
(response) => {
endLoading()
return response
},
(error) => {
Message.error(error.response.data)
endLoading()
const { status } = error.response
if (status === 401) {
Message.error('Please Login')
this.$store.dispatch('LogOut')
router.push('/login')
}
return Promise.reject(error)
}
)
export default axios
components/NavBar.vue
<script>
export default {
name: "NavBar",
computed: {
isLoggedIn: function () {
return this.$store.getters.isAuthenticated;
},
username: function () {
return this.$store.getters.getUsername;
},
fullname: function () {
return this.$store.getters.getFullname;
},
},
methods: {
async logout() {
await this.$store.dispatch("LogOut");
this.$router.push("/login");
},
},
};
</script>
I have made lots of research and since keywords are always similar I cannot find a correct way of usage store modules in nuxtjs. I will shorten the codes to make it readable. In my nuxtjs application, I am trying to reach my vuex modules in my home.page but instead I get
pages/index.vue
TypeError
Cannot read property 'then' of undefined
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
.then(() => {
While creating modules first in the store folder i have created an index.js file:
import Vuex from "vuex";
import articles from "./modules/articles";
// ...
import videolessons from "./modules/videolessons";
const debug = process.env_NODE_ENV !== 'production';
export const store = new Vuex.Store({
modules: {
articles,
books,
members,
pages,
status,
user,
videolessons,
},
strict: debug,
plugins: [],
})
and basically my modules are similar to my articles module:
const getDefaultState = () => {
return {
indexArticles: [],
}
}
const state = getDefaultState()
const getters = {
indexArticles (state) {
return state.indexArticles
},
}
const mutations = {
fetchStart (state) {
state.loading = true
},
fetchEnd (state) {
state.loading = false
},
setIndexArticles (state, pArticles) {
state.indexArticles = pArticles
state.errors = {}
},
setError (state, errors) {
state.errors = errors
},
resetState (state) {
Object.assign(state, getDefaultState())
}
}
const actions = {
// ...
async fetchIndexArticles ({ commit }) {
try {
const response = await articlesService.fetchIndexArticles()
commit('fetchStart')
commit('setIndexArticles', response.data)
commit('fetchEnd')
return response
} catch (error) {
commit('setError', error)
this._vm.$q.loading.hide()
}
},
...
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
and in my index page:
<script>
import store from '../store/'
export default {
computed: {
indexarticles() {
return this.$store.getters['articles/indexArticles'];
}
},
created() {
this.$store.dispatch('articles/fetchIndexArticles')
.then(() => this.$store.dispatch('videolessons/fetchIndexVideolessons'))
...
.then(() => {
this.isLoading = false;
});
}
};
</script>
<template>...</template>
can you help to correct my store modules?
Thanks
ps:
videolessons.js
const getDefaultState = () => {
return {
indexvideolessons: [],
}
}
const state = getDefaultState()
const getters = {
indexVideolessons (state) {
return state.indexvideolessons
},
}
const mutations = {
setIndexVideolessons (state, pVideolessons) {
state.indexvideolessons = pVideolessons
state.errors = {}
},
}
const actions = {
async fetchIndexVideolessons ({ commit, dispatch }) {
try {
const response = await videolessonsService.fetchIndexVideolessons()
commit('setIndexVideolessons', response.data)
return response
} catch (error) {
commit('setError', error)
}
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
in a react-native project,I keep hitting in this error:
state is undefined, evaluating store.getstate()
//store.js
const composeEnhancers =
typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const enhancer = composeEnhancers(applyMiddleware(thunk));
const Store = createStore(
combineReducers(
{
form: formReducer,
appointmentsReducer
},
enhancer
)
);
console.log(Store.getState());
export default Store;`
//reducer.js
import {
FETCH_APPOINTMENTS_BEGIN,
FETCH_APPOINTMENTS_SUCCESS,
FETCH_APPOINTMENTS_FAILURE
} from '../actions/appointmentsAction';
const initialState = {
data: [],
loading: false,
error: null
};
export default function appointmentsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_APPOINTMENTS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_APPOINTMENTS_SUCCESS:
return {
...state,
loading: false,
data: action.payload.appointments
};
case FETCH_APPOINTMENTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
data: []
};
default:
return state;
}
}
//actions.js
import { listUrl } from "../cst";
export const FETCH_APPOINTMENTS_BEGIN = "FETCH_APPOINTMENTS_BEGIN";
export const FETCH_APPOINTMENTS_SUCCESS = "FETCH_APPOINTMENTS_SUCCESS";
export const FETCH_APPOINTMENTS_FAILURE = "FETCH_PRODUCTS_FAILURE";
export const fetchAppointmentsBegin = () => ({
type: FETCH_APPOINTMENTS_BEGIN
});
export const fetchAppointmentsSuccess = appointments => ({
type: FETCH_APPOINTMENTS_SUCCESS,
payload: { appointments }
});
export const fetchAppointmentsFailure = error => ({
type: FETCH_APPOINTMENTS_FAILURE,
payload: { error }
});
export function fetchAppointments() {
return dispatch => {
dispatch(fetchAppointmentsBegin());
return fetch(listUrl)
.then(handleErrors)
.then(res => res.json())
.then(json => {
dispatch(fetchApointmentsSuccess(json.appointment));
return json.appointment;
})
.catch(error => dispatch(fetchAppointmentsFailure(error)));
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
// app.js
export default function App() {
return (
<Provider store={Store}>
<Navigation />
</Provider>
);
}
//list rendrer component :
const mapStateToProps = state => ({
data: state.appointments.data,
loading: state.loading,
error: state.error
});
the console.log of store.getstate() gives :
Object {
"appointmentsReducer": Object {
"data": Array [],
"error": null,
"loading": false,
},
"form": Object {},
I'm not sure where the problem is.
Is it due to the asynchronous call not being handled properly?
If I use saga to handle the fetch, will it resolve the problem?
Any help would be appreciated .
I am using nuxtjs, axios and vuex to post from a form component to post my data to my backend.
When posted I'd like to redirect to the view record screen and populate it with the returned information using the ID to navigate there
so my path might be /cases/14325 (if 14325 is the id returned once created)
What is the correct way to do this please
I have the following code in my vuex store
export const state = () => ({
cases: []
})
// *** MUTATIONS ***
export const mutations = {
add(state, newCase ) {
state.cases.push(newCase)
},
}
// *** ACTIONS ***
export const actions = {
addCase(context, newCase) {
const createdCase = {
...newCase
}
axios.post("http", createdCase)
.then(result => {
context.commit('add', {...createdCase, id: result.data.name})
})
.catch(e => console.log(e));
},
}
In my component I have the following
import { mapMutations, mapGetters, mapActions } from 'vuex'
export default {
data () {
return {
newCase: {
caseName: '',
summary: '',
status: 'live',
},
}
},
methods: {
...mapActions([
'addCase'
]),
onSubmit() {
// Save the post
this.$store.dispatch('addCase').then(path => {
this.$router.redirect(path)
}).catch((err) => {
console.log(err)
})
},
}
}
</script>
How do i return the new id from my store please and replace cases/1 with '/cases/' + new id?
Thanks for the help as always
Maybe is will be enough when you improve your action this way:
addCase(context, newCase) {
return new Promise ((resolve, reject) => {
const createdCase = {...newCase}
axios.post('http', createdCase).then(result => {
context.commit('add', {...createdCase, id: result.data.name})
resolve(/*path*/)
}).catch(e => {
console.log(e)
reject(/*reason*/)
})
})
}
And then you use it this way:
this.$store.dispatch('addCase', context).then(path => {
this.$router.redirect(path)
})