I use action to send a request and return the result to reducer, but store can't receive the result - redux

when componentDidMount i dispatch an action to send a request, this is the action code below. receiveUserInfo just return data to reducer
export function requestUserInfo() {
return dispatch => {
dispatch(getUserInfo())
return axios.post('/user/getScores', qs.stringify({
token: token,
uid: uid
}))
.then(res => {
if (res.data.status !== 200) {
message.error(res.data.message)
return state
}
// return the data from server
return { ...res.data.attachment}
})
.then(data => {
// receiveUserInfo just return action and data
dispatch(receiveUserInfo(data))
})
}
}
this is the reducer code.
const token = localStorage.getItem('token')
const uid = localStorage.getItem('uid')
export const requestUserInfo = (state = {}, action) => {
switch (action.type) {
case 'HEADER_GET_USERINFO':
case 'HEADER_RECEIVE_USERINFO':
return Object.assign({}, state, action.data)
break
default:
return state
}
}
function receiveUserInfo(data) {
return {
type: 'HEADER_RECEIVE_USERINFO',
data
}
}

Related

update value of an element of object array in redux store

There is a challenge update existing elements value in json array in redux store by an action creater.
You can run code here also shared it below;
console.clear()
const CreateTeam = (team, point) => {
return {
type:"CREATE_TEAM",
payload: {team, point}
}
}
const UpdateTeam = (team, point) => {
return {
type:"UPDATE_TEAM_POINT",
payload: {team, point}
}
}
const TeamReducer = (state = [], action) => {
if(action.type == "CREATE_TEAM")
{
return [...state, action.payload]
}
if(action.type == "UPDATE_TEAM_POINT")
{
let point=action.payload.point;
return [...state, {
...state.teams,
point:point
}]
}
return state;
}
const { createStore, combineReducers } = Redux;
const league = combineReducers({
teams: TeamReducer
})
const store = createStore(league);
store.dispatch(CreateTeam("TeamA",10));
store.dispatch(CreateTeam("TeamB",20));
store.dispatch(UpdateTeam("TeamA",15));//not work
console.log(store.getState())
create actions works fine, I expected the point value of TeamA set to 15.. but its added new object has only "point" property value 15
There is an error in name of actionTypes:
action dispatches type:"UPDATE_TEAM"
reducer handles action.type == "UPDATE_TEAM_POINT"
You have to perform immutable change, try this:
const TeamReducer = (state = [], action) => {
if(action.type == "CREATE_TEAM")
{
return [...state, action.payload]
}
if(action.type == "UPDATE_TEAM")
{
const {team, point} = action.payload;
const changedIdx = state.findIndex((item) => item.team === team);
return [...state.slice(0, changedIdx), action.payload, ...state.slice(changedIdx + 1)]
}
return state;
}

Async / Await Vuex

I want to call an action in created hook, wait until is done and in same hook to display the result. Is that possible?
I tried to put async / await in actions but doesn't help.
This is the action property with the async function in the store:
actions: {
async FETCH_USER({commit}) {
await firebase.firestore().collection('test').get().then(res => {
commit('FETCH_USER', res.docs[0].data())
})
}
}
created() {
this.FETCH_USER()
console.log(this.GET_USER)
},
methods: {
...mapActions([
'FETCH_USER'
]),
login() {
if(this.$refs.form.validate()) {
console.log('welcome')
}
}
},
computed: {
...mapGetters([
'GET_USER'
])
}
export default new Vuex.Store({
state: {
user: null
},
getters: {
GET_USER: state => state.user
},
mutations: {
FETCH_USER(state, user) {
state.user = user
}
},
actions: {
FETCH_USER({commit}) {
firebase.firestore().collection('test').get().then(res => {
commit('FETCH_USER', res.docs[0].data())
})
}
}
})
async/await version
async FETCH_USER({ commit }) {
const res = await firebase.firestore().collection('test').get()
const user = res.docs[0].data()
commit('FETCH_USER', user)
return user
}
async created() {
// The action returns the user out of convenience
const user = await this.FETCH_USER()
console.log(user)
// -- or --
// Access the user through the getter
await this.FETCH_USER()
console.log(this.GET_USER)
}
You need to await the action call because it is an async function.
Promise version
FETCH_USER({ commit }) {
return firebase.firestore().collection('test').get().then(res => {
const user = res.docs[0].data()
commit('FETCH_USER', user)
return user
})
}
created() {
this.FETCH_USER().then(user => {
console.log(user)
})
// -- or --
this.FETCH_USER().then(() => {
console.log(this.GET_USER)
})
}

dispatching inside actions in action creator

Can we use dispatch inside action creators and what purpose do they serve inside action creators ?
Here is a sample modified code from codebase .
export default function xyz(data) {
const url = ;
return function (dispatch) {
dispatch(
a()
);
callApi(url, REQUESTS.POST, HEADERS, data).then((response) =>{
dispatch(
b(data)
);
}).catch((error) => {
dispatch(
c(error.toString())
);
});
};
}
// this returns a type (an object)
export function a() {
return {
type: xyzzzz
};
}
Similarly we have b and c returning either type or say objects .
Yes, you can dispatch multiple actions in an action.
I usually put a dispatch on an asychnronous action like this
function action() => {
return async (dispatch) => {
let payload;
dispatch('before-request');
try {
await someAsyncProcess();
payload = { status: 'success' };
} catch (err) {
payload = { status: 'failure' };
}
dispatch('after-request', payload);
};
}

Failed to store data in redux store

.created a store
.created actions and reducers
.connected store to my component
.failed to store data in redux store
i have to store the add events in a calendar using redux ,the values are not stored in redux .
// Actions
export const saveUserEvents=(events)=>{
return {
type:'SAVE_USER_EVENTS',
payload: events
}
}
//Reducers
export const form=(state = initialState, action) => {
switch (action.type) {
case "SAVE_USER_EVENTS":
return {...state, events: action.payload};
default:
return state
}
}
// Store
let reducers=combineReducers({
form
})
export default createStore(
reducers,
composeWithDevTools(
applyMiddleware(thunk)
)
)
// Component
Form(){
var title = document.getElementById("title").value
var description = document.getElementById("description").value
var start = document.getElementById("start").value
var end = document.getElementById("end").value
if(title!=""){
alert("Event Added Successfully")
this.props
.form({
variables: {
title:title,
description:description,
start:start,
end: end,
user_id: localStorage.getItem("id")
}
})
.then(({ data }) => {
alert("data is "+JSON.stringify(data.form))
if(data.form.message == "Event saved"){
this.props.saveUserEvents(data.form);
//alert("success")
//this.props.history.push('/Calender')
window.location.href="/Calendar";
return true;
}else {
alert("failure")
document.getElementById("messagegeneralNumber").innerHTML = '';
}
})
.catch(error => {
alert("data is "+JSON.stringify(error))
})
}
else{
alert("data not loading")
}
}
//connected to store-redux. using mapstatetoprops and mapdispatchtoprops
const mapStateToProps=(state, ownProps)=>{
return {
form: state
}
}
const mapDispatchToProps={
saveUserEvents
}
const Event = compose(
graphql(mutations.FORM,{name:'form'})
)(AddEvent);
export default withRouter(connect(mapStateToProps
,mapDispatchToProps)
(Event));
Your mapDispatchToProps should look like this:
const mapDispatchToProps = (dispatch) => {
saveUserEvents: (events)=>{
dispatch(saveUserEvents(events)); <--- here is your action creator that have to be dispatched
}
}

redux observable: Why don`t can get all actions in test

I'm trying to test a 'redux observable epic' but the test fail because not all actions are in store.getActions() the strange is the store.dispatch function runs.
Epic and actions
export const VERIFY_SESION = 'auth/VERIFY_SESION';
export const SET_POLICIES_ACCEPTED = 'auth/SET_POLICIES_ACCEPTED';
export const AUTHENTICATE = 'auth/AUTHENTICATE';
export function setPoliciesAccepted(wereAccepted: boolean) {
return {
wereAccepted,
type: SET_POLICIES_ACCEPTED,
};
}
export function verifySesion() {
return {
type: VERIFY_SESION,
};
}
export function authenticate(token) {
return {
token,
type: AUTHENTICATE,
};
}
export function verifySesionEpic(action$, store) {
return action$
.ofType(VERIFY_SESION)
.switchMap(async () => {
try {
store.dispatch(setBlockLoading(true));
const token = await AsyncStorage.getItem('token');
if (token !== null) {
store.dispatch(setBlockLoading(false));
return authenticate(token);
}
const policiesWereAccepted = await AsyncStorage.getItem('policiesWereAccepted');
store.dispatch(setBlockLoading(false));
return setPoliciesAccepted(policiesWereAccepted);
} catch (error) {
return setMessage(error.message);
}
});
}
test
describe('actions/auth', () => {
let store;
const asyncStorageGetStub = stub(AsyncStorage, 'getItem');
beforeEach(() => {
store = mockStore();
});
afterEach(() => {
asyncStorageGetStub.restore();
});
it('Should call authenticate if token', () => {
const token = 'mitoken';
asyncStorageGetStub.withArgs('token').returns(Promise.resolve(token));
store.dispatch(verifySesion());
expect(store.getActions()).toContain({ type: AUTHENTICATE, token });
});
});
Test result
1) "actions/auth Should call epic for verifySesion:
Error: Expected [ { type: 'auth/VERIFY_SESION' } ] to include { token: 'mitoken', type: 'auth/AUTHENTICATE' }"
Note
im sure that the conditional token !== null pass
I was to add a timeout before getAction because the 'AUTHENTICATE' actions is added after.
it('Should call authenticate if token', (done) => {
const token = 'mitoken';
asyncStorageGetStub.withArgs('token').returns(Promise.resolve(token));
store.dispatch(verifySesion());
setTimeout(() => {
expect(store.getActions()).toContain({ type: AUTHENTICATE, token });
done();
}, 1000);
});

Resources