I have simple flow in my app. After request is finished i'm calling an action with a payload:
export const setVoteFlagCurrentPoll = () => dispatch => {
dispatch({
type: SET_VOTE_FLAG_CURRENT_POLL,
payload: true
})
}
Then in reducer im changing one variable in a poll object which looks like this:
{
idsurvey: 10,
topic: "random poll question",
answers: Array(4),
voted: false
}
Reducer itself:
case SET_VOTE_FLAG_CURRENT_POLL:
return {...state, pollData: {...state.pollData, voted: action.payload}};
My issue is that variable 'voted' is not chaning its value. Its still the same which is 'false'. Interesting thing is I can just log that recuder as: console.log({...state, pollData: {...state.pollData, voted: action.payload}}); and it works.. its logging with voted as true. Why is this happening?
Ok, I figured it out. It seems like mapStateToProps function was badly written..
before (not working):
const = ({user}) => ({user}); // its returning whole user reducer
after (working now):
const mapStateToProps = state => {
return {
user: state.user //same as above (I didnt want to change the code in other components..)
pollData: state.user.pollData //pulling pollData directly
}
}
Hope it helps.
Related
I am trying to understand some socket and props behavior I'm seeing in my react-native app. I am using a redux store in this app. When the user manually signs out, I call the following:
if (this.props.authenticated) {
this.props.deauthenticateUser().then((res) => {
console.log('res: ', res); // {"payload": {"authenticated": false, "error": ""}, "type": "DEAUTHENTICATE_USER"}
console.log('this.props.authenticated 26: ', this.props.authenticated); // is true here, should be false
if (!this.props.authenticated) this.props.navigation.navigate('Login'); // Never happens because authenticated is still true
});
}
}
By the way, the redux section of that page/screen looks like this:
// Redux Mapping
const mapStateToProps = (state) => {
return { ...state.User };
};
const mapDispatchToProps = (dispatch) => ({
deauthenticateUser: async (user) => await dispatch(deauthenticateUser(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(LogoutScreen);
This is what the deauthenticateUser function looks like:
export const deauthenticateUser = () => {
console.log('deauthenticateUser() firing...'); // I see this
return async (dispatch) => {
await SocketDriver.Deauthenticate(); // This is successful
await LocalDataStore.Invalidate(); // This is successful
// Clear unauthorized application state
dispatch(clearOverview());
dispatch(clearSignature());
dispatch(clearUser());
console.log('finished clearing data...'); // I see this
return {
type: DEAUTHENTICATE_USER,
payload: { authenticated: false, error: '' },
};
};
};
This calls my User reducer, which looks like this:
case 'DEAUTHENTICATE_USER':
return { ...state, ...payload };
Even when I try a setTimeout to give plenty of time for deauthenticateUser() to complete, I find that authenticated never gets to a false state.
What am I potentially missing here? The logic seems to look solid to my eyes. Nothing bombs out, and when I console.log the data out at various points, it all looks as it should, EXCEPT for the props.authenticated property, which remains set to true.
Is it possible that my Logout screen doesn't get the final props update? And, if so, why would that be? Even when I try to call the deauthenticateUser() function multiple times -- just to test it -- I never see this.props.authenticated ever console.log as false.
Objective
I am trying to pass some objects I get back from Firestore into my reducer so I can display some results back to the user.
Problem
When I try to call and pass the query to the reducer it does not appear to work. I am running a console.log to see if the reducer gets called but nothin it appearing. I think is because I have nested return statements?, Is this true?
Here is my action:
export const queryBidder = (value) => {
return async (dispatch, getState, { getFirestore }) => {
const firestore = getFirestore();
const normalizeValue = _.capitalize(value);
let Query = []
firestore.collection("bidders").where("firstname", ">=", normalizeValue).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching bidders.');
return;
}
snapshot.forEach(doc => {
Query.push(doc.data());
return { type: actionTypes.QUERYBIDDER, Query: Query };
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
};
When I put the return statement above the async return statement, all works well. However, I need to be able to call getFirestore(). This setup comes from a tutorial I found on Udemy, to be honest I don't quite understand it.
return async (dispatch, getState, { getFirestore })
How are these getting passed in? Where do they come from? Why does the order matter? How can I just access the getfirestore() function without wrapping my actions logic in this function?
I am unsure of why this worked but while awaiting for responses I opted to change from return to dispatch.
dispatch({ type: actionTypes.QUERYBIDDER, Query: Query });
this resolved my issue. Look for a more thorough answer of why this worked and my above original questions.
From redux documentation about using dispatch - This is the only way to trigger a state change. So basically if you want to change redux state you should dispatch an action with parameters - action type and new data.
You can read more about redux dispatch here
I have an icon that when clicked it triggers a function that calls an API and then dispatch an action to remove a blog post from the state. But the problem is that my UI does not re-render. However, if I refresh my browser the post that I deleted is no longer there and my store matches my state.
Here is my function that calls an API and then dispatch an action:
export function deletePost(postID) {
return (dispatch) => {
fetch(`${url}/posts/${postID}`, { method: 'DELETE', headers})
.then((response) => response.json())
.then((postID) => dispatch(removePost(postID)))
.catch((error)=>{console.log('dispatch error',error)});
};
Here is my action:
export function removePost ( postID ) {
return {
type: REMOVE_POST,
postID,
}
}
And here is my reducer:
function posts (state = {}, action) {
switch (action.type) {
case REMOVE_POST:
return [
...state.filter((post)=>post.id!==action.postID)
];
default:
return state
}
}
Now when I simply dispatch an action without calling an API
export function deletePost(postID) {
return (dispatch) => {
dispatch(removePost(postID));
}
My state is correctly updated but of course my redux store is not. When I do the calling of API before dispatching an action as shown earlier, there is also no error coming from the console log.
What could be the problem here? I am very new to ReactJS and can't find a solution yet to this problem after many tries.
Also, as a note, I am using redux-thunk in this project.
I have a few questions, but first: I think the problem is here:
[
...state.filter((post)=>post.id!==action.postID)
]
What is the state's shape? Is it state = [post1, post2, ...]? I can see the initial state is {}, so I find it weird to be calling state.filter and not state.posts.filter or whatever here.
The other might be problem, is with post.id !== action.postID, maybe the received ID is an number type, and maybe the local id is a string? Maybe the other way around?
I am writing my first bigger React/Redux/Meteor App. I know that Redux is not necessarily needed in an Meteor App, but I want to use it.
I load a record from a MongoDB with Meteor and then I want to store this object in my Redux store. But the object gets nested in the store and I do not know why this is the case.
Here is my code so far:
Action loads the remote record
export const loadRecord = (id) => {
return dispatch => {
Meteor.call('loadRecord', id, (error, result) => {
if (!error) {
dispatch({
type: TYPE,
result
});
} else {
dispatch({
type: TYPE_ERROR,
error,
});
}
});
};
};
Reducer should update my store
const initialState = {
singleRecord: {}
};
export function singleRecord(state = initialState, action) {
switch (action.type) {
case TYPE:
return {
...state,
singleRecord: action.result
};
default:
return state;
}
}
In more store I expect something like this:
singleRecord: {
id:"1223",
text:"abcde"
}
But what I get is:
singleRecord: {
singleRecord {
id:"1223",
text:"abcde"
}
}
So my store gets updated and everything is working as expected beside the fact, that my record is nested somehow.
I think I am missing a fundamental thing, or I implemented it wrong. It would be very nice if someone can explain me if this is the intended behavior or if not, can tell me why my code is not working as intended.
Thanks in advance
You want to unwrap the payload of the action:
return {
...state,
...action.result
};
or, in other words:
return Object.assign({}, state, action.result);
I am not sure what else you want to save in singleRecord but it's entirely possible you want to do this:
return action.result;
Also, your initial state should be just const initialState = {};
The object returned from your singleRecord reducer is what is stored into singleRecord state.
For last two weeks I have been working with redux and I'm facing an issue where I want to access/change a state value of another reducer. How can I achieve that?
For example: I have two components 'A-Component' and 'Message-component'
which has 'A-actions', 'Message-actions' and 'A-reducer', 'Message-reducer' respectively
When an action of 'A-Component' is called it will call the corresponding reducer function where I need to update the Message-reducer state value which will display the message box
A-action
export function add(data) {
return {
types: [types.ONADD, types.ONADDSUCCESS, types.ONADDFAIL],
payload: {
response: api.add(data).then(response => response),
data
}
};
}
A-reducer
export default createReducer(initialState, {
[types.ONADD](state) {
return {
...state,
message: 'Updating Records'
};
}
});
The above mentioned message state value is message reducer's state value. I want to update the message state value from A-reducer
which in turn updates the message component. Is this possible in redux?
I tried with various middleware but failed.
Thank in advance!
I think you're approaching this the wrong way. You should normalize your data as much as you can, and then maybe use the connect decorator to compose the state you need for your UI. For example, Messages could be nested under a "Friend"'s node, but it's better to have them in their own store, and then make a function that selects the messages from a friend based on a relationship. This gives you aggregations (You have 3 unread messages) for free. Take a look at reselect for a way to do this in a nice (and cached) way.
Edit:
You could write middleware which dispatches multiple actions:
export default (store) => (next) => (action) => {
if(!action.types){
return next(action);
}
action.types.forEach(type => {
next({
type,
payload: action.payload
})
});
}
Then call it from an Action Creator like so:
export function addMessage(message){
return {
types: ['ADD_MESSAGE', 'UPDATE_USER'],
payload: message
}
}
If you already have a update action in Message-actions
I think you can just directly dispatch the update action when ONADDSUCCESS is triggered.
// Message action
export function MessageUpdate (data) {
return {
type: ...,
data,
}
}
// A action
export function add(data) {
return dispatch => {
dispatch({
type: types.ONADD
});
// code for your add event
api.add(data).then( response => {
(() => {
dispatch(MessageUpdate(response));
return dispatch({
type: types.ONADDSUCCESS,
})
})()
});
}
}
Hope this answer to your question.