RTK startListening will accumulate the effects? - redux

For example:
export const authMiddleWare = createListenerMiddleware<TRootState>();
const startAuthMiddleWare = authMiddleWare.startListening as TListener;
startAuthMiddleWare({
actionCreator: actionSetToken,
effect: (_, {dispatch, getState}) => {
favoriteProductsLoggedInEffect(dispatch, getState);
cartLoggedInEffect(dispatch, getState);
},
});
startAuthMiddleWare({
actionCreator: actionFlushToken,
effect: () => {
AuthService.flushTokens();
},
});
First I'm subscribe to actionSetToken and after that I've subscribe to actionFlushToken trough single startAuthMiddleWare method. Is it safe? Can I override the actionSetToken-subscrib by subscribing to actionSetFlushToken?

Related

Redux state doesn't update immediately

I have super simple question
Why my redux state doesn't update immediately?
const { reducer, actions } = createSlice({
name: "professionals",
initialState: {
loading: false,
lastFetchList: undefined,
list: undefined,
professional: undefined,
filters: {
virtual: false
}
},
reducers: {
professionalsListRequested: (professionals, action) => {
if (action.payload.withLoading) professionals.loading = true;
},
professionalsListRequestFailed: (professionals, action) => {
professionals.loading = false;
},
professionalsListReceived: (professionals, action) => {
professionals.lastFetchList = Date.now();
professionals.list = action.payload.data.dataArr;
professionals.loading = false;
},
virtualUpdated: (categories, action) => {
categories.filters.virtual = action.payload;
}
},
});
export const { virtualUpdated } = actions;
export default reducer;
it is my slice.
and here is code of the component :
const dispatch = useDispatch();
const filters = useSelector((state) => state.professionals.filters);
const handlePressOnVirtual = async () => {
console.log("Before" , filters.virtual)
await dispatch(virtualUpdated(!filters.virtual));
console.log("after" , filters.virtual)
};
when handlePressOnVirtual function is called the both console.log(s) print previous value of the state.
When you are still in handlePressOnVirtual function, you are still in a closure, so all the references will still be your existing filters
So you would need to wait for another re-render for useSelector to invoke again then the new values will come.
One way to see the latest changes is to put your log inside a useEffect:
useEffect(() => {
console.log("after" , filters.virtual)
},[filters.virtual]);

because my return is [{"singles": [[Object], [Object]]}]

I am using a useEffect to get information from a collection and a sub-collection, however, the data return from my sub-collection is being [{"singles": [[Object], [Object]]}]
useEffect(() => {
fireStore
.collection('users')
// .where('category','==','band')
// .where('status','==','approved')
.where('email','==','marcelomenoli12#gmail.com')
.get()
.then( async (musics) => {
const allSingles = musics.docs.map(async (single) => {
const items = await single.ref.collection('musics').get();
return {
// cover: single.data().cover,
// name: single.data().bandArtistName,
singles: items.docs.map((item) => ({
id: item.id,
...item.data(),
}))
}
});
const filteredSingles = await Promise.all(allSingles);
setDATA(filteredSingles);
});
}, []);
console.log(DATA);
You should use more than one then clauses, so every async operation return values. Otherwise inner await responses are not come on time.
I tried to update your code. I hope that helps.
useEffect(() => {
fireStore
.collection('users')
.where('email','==','marcelomenoli12#gmail.com')
.get()
.then( async (musics) => {
const allSingles = musics.docs.map(async (single) => {
return await single.ref.collection('musics').get();
})
.then((items)=>{
// cover: single.data().cover,
// name: single.data().bandArtistName,
singles: items.docs.map((item) => ({
id: item.id,
...item.data(),
}))
})
}, []);
console.log(DATA);

Dispatch in middleware leads to action with wrong type

I'm trying to create a simple middleware to handle socket events.
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
});
};
I dispatch this action that triggers it. And now when the dispatch method was called in my middleware with type 'ACTION-2' and received socketData as a payload, I see in my console what 'ACTION-1' was triggered twice and in the last time it is came with my socketData payload.
I wonder why 'ACTION-1' was registered instead 'ACTION-2' and how I can fix it? I would appreciate your help.
import { socket } from 'services/socket';
const socketMiddleware = ({ dispatch }) => next => (action) => {
const {
channel,
events, // an array of events for the channel
...rest
} = action;
if (typeof action === 'function' || !channel) {
return next(action);
}
const {
type,
name,
} = channel;
const channelInstance = socket.instance[type](name);
events.forEach((event) => {
const handleEvent = (socketData) => {
dispatch({ type: 'ACTION-2', socketData, ...rest });
};
channelInstance.listen(event.name, handleEvent);
});
return next(action);
};
export {
socketMiddleware
};
looks like you are not pathing the channel in your initial dispatch and you are failing your middleware finishes inside this if:
if (typeof action === 'function' || !channel) {
return next(action);
}
in order to fix this you should add channel in your dispatch:
const join = (channel) => (dispatch) => {
dispatch({
type: 'ACTION-1',
socketChannel: {...},
events: [...],
channel: { type: '...', name: '...' }
});
};

shouldn't I dispatch an action inside a .then statement?

I found a code on git which I'm trying to understand and in the code the guy have this function:
export function startAddTodo(text) {
return (dispatch, getState) => {
const UID = firebase.auth().currentUser.uid;
const todo = {
text,
isDone: false,
isStarred: false
};
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
dispatch(addTodo({
id: todoRef.key,
...todo
}));
todoRef.then(snapshot => {
return;
}, error => {
Alert.alert(JSON.stringify(error.message));
});
};
}
Why shouldn't it be like
const todoRef = firebaseRef.child(`todos/${UID}`).push(todo);
todoRef.then(snapshot => {
dispatch(addTodo({
id: snapshot.key,
...todo
}));
})
I think this because the promise may be rejected, but in the first code he may get an error when trying to call todoRef.key inside the dispatch method.

multiple dispatch in redux action

I wanted to dispatch an action from another action but not able to do so. When I try to do so it not able to found getAllUser method.
Below is my action class.
export const myActions = {
getAllUser() {
return (dispatch) => {
makeApiCall()
.then((response) => {
dispatch({
type: USER_SUCCESS,
payload: response,
});
})
.catch((error) => {
dispatch({
type: USER_FAILURE,
payload: error,
});
});
};
},
addUser(user) {
return (dispatch) => {
makeApiCall(user)
.then((response) => {
/*
Need help here :
wants to call above getAllUser()
.then(() =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
)
*/
};
},
};
I have tried various approaches like,
myActions.getAllUser()
.then((response) =>
dispatch({
type: ADD_SUCCESS,
payload: response,
});
);
and trying do dispatch directly,
const self = this;
dispatch(self.getAllUser());
dispatch({
type: ADD_SUCCESS,
payload: response,
});
One more way around this is after addUser success, update the reducer and than from UI call getAccount again to refresh the results, but just curious to know on how can I achieve this using multiple dispatch.
You can export the functions individually instead of wrapping it under the same object:
export const getAllUser = () => dispatch => { ... }
export const addUser = () => dispatch => {
...
dispatch(getAllUser());
}
You can still import them all if desired:
import * as myActions from '...';
Or you can declare getAllUser first then add to myActions, but the above solution is much cleaner.
const getAllUser = ...
const myActions = {
getAllUser,
addUser = ... { dispatch(getAllUser()) }
}

Resources