Redux-saga: dispatch does not trigger saga sometimes [closed] - reactjs

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 1 year ago.
Improve this question
I have two pages named Login and Welcome. I noticed that I cant run dispatch before login.
To be more understandable I leave an example below;
On Login Page dispatch(fetchUser())) doesn't trigger saga
Login Page =>
const fetchUserLocal = () => {
console.log("dispatch run, before user login: ",dispatch(fetchUser()))
dispatch(fetchUser())
}
return ( <div> <button onClick={fetchUserLocal}>Click to FETCH USER</button> </div>)
On Welcome Page dispatch(fetchUser())) trigger the saga
(The page can be accessible after logging in) Welcome Page =>
const fetchUserLocal = () => {
console.log("dispatch run, after user login: ",dispatch(fetchUser()))
dispatch(fetchUser())
}
return ( <div> <button onClick={fetchUserLocal}>Click to FETCH USER</button> </div>)
Both pages have useDispatch and action and I can reach on these pages.
According to your request I can add more information,
thanks for help

I figure out the problem, It's all my mistake.
Welcome page is working because when fetchUser in that page I have a session, and my backend API return success and the code runs in the try block.
But I don't have any session when I fetch the user on Login page, and backend API returns credential error, and code runs in catch.
The problem start here because I have missed parantesies. It works now well when I change yield put(fetchUserFailure) to yield put(fetchUserFailure())
export function* fetchUser() {
try {
console.log("fetchUser")
const user = yield axios.get(FETCH_USER, { withCredentials: true });
console.log("saga outh user",user)
yield put(fetchUserSuccessful(user.data));
} catch (error) {
console.log("error",error)
yield put(fetchUserFailure);
}
}

I was using a helper package for actions and reducers. The package name is react-act. I don't know why but when I removed this package I didn't get the same error. Everything is okay now.
If I learn the what I did wrong with react-act I will leave a comment here, if you already know or have a guess, please leave a comment.

Related

React-Native unable to store state getting null

I'm new to React-native so if there is a misunderstanding please be super clear and treat me as if I have never seen React-native before.
I have the app so that when you press on a button it will send you into an Auth0 flow where you can log in to the app. This seems working. If I log out the access token directly in the callback I am successful in getting it at the credentials.accessToken variable/location. However, when I am trying to set the state of the accessToken variable I get back null when I try to log it out to the screen via an alert or even via console.log. What am I doing wrong to cause this? I tried searching SO and google but both seem to show this as the right way of doing it.
Code:
const [accessToken, setAccessToken] = useState(null)
const onLogin = () => {
auth0.webAuth
.authorize({
scope: 'openid profile email'
})
.then(credentials => {
setAccessToken(credentials.accessToken)
Alert.alert('Access token: ', accessToken)
})
.catch(error => console.log(error)) // Eventually send this to backend for crash reporting
}
This is probably a case of a state not updating immediately. Try to use a useRef() instead of a useState(https://www.w3schools.com/react/react_useref.asp). If the problem is solved the issue was with the fact that states are updated asynchronously and hence it was not set to its most recent value (the value you expected) when you console logged it.

setPersistence function is not working properly (Firebase + React)

I want to make a functionality where I persist the user while the window is open or to be precise in the current session. To make this I've researched the Firebase documentation, and I found out about this: Authentication State Persistence.
After researching it, I decided to put it inside my app, here's how my code looks like:
function logIn(email, password) {
setPersistence(auth, browserLocalPersistence)
.then(() => {
return signInWithEmailAndPassword(auth, email, password);
})
.catch((error) => {
console.log(error);
});
}
This function is inside my UserAuthContext file where I keep all functionalities regarding user. Now some may say that there is nothing wrong with the way this function is made, but whenever email and password are empty the user can still Login without being stopped by Firebase errors like it happens without setPersistence function.
Does anyone know why does my Login go through even though email and password are empty?

React Native firebase [firestore/permission-denied] [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
I have ploblems with English. I apologize in advance.
Problems with firestore and auth
Error: [firestore/permission-denied] The caller does not have permission to execute the specified operation.
NativeFirebaseError: [firestore/permission-denied] The caller does not have permission to execute the specified operation.
My rules in FireStore:
rules_version = '2';
service cloud.firestore {
match/databases/{database}/documents {
match /Users/{document=**} {
allow read, get: if true;
allow create, update: if request.auth.uid == resource.id;
}
}
I using npm package:
#react-native-firebase/app
#react-native-firebase/app-check
#react-native-firebase/auth
#react-native-firebase/firestore
My code:
import auth from '#react-native-firebase/auth';
import firestore from '#react-native-firebase/firestore';
async function onAuthChanged(onChange) {
auth().onAuthStateChanged(onChange);
}
async function authenticateUser(status) {
if (status) {
const uid = status.uid;
let user = await firestore().collection('Users').doc(uid).get(); // Error
return ({
user: {...user.data(), uid} ?? {login: undefined, birthday: undefined, uid}
});
} else {
return { user: null };
}
}
onAuthChanged(async (status) => {
const { user } = await authenticateUser(status);
});
P.S. In fireStore my rules work: enter image description here
P.S.S. This is my first time working with Firebase and everything worked for the first two weeks with standard rules, but today it gives an error. and I do not know why. Although they offer me to put true on all the rules. This does not help in any way for 6-7 hours I have been trying to understand, so I have already turned here.
In firestore, if you got any permission denied. This is because firestore security rules.
Change your rules to:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}

update state of the parent screen if an update occurs in current screen

I am using react-navigation 6 and react-native CLI, to make a chat application. I want to achieve this feature which is common in every chat application that when we send a message to someone, and go back to homescreen of the app (where all conversations are listed), the last message sent, can be seen.
Like if I sent message and pressed the back button, it will navigate me to home screen where all my conversations are, and it should update the conversation where I sent the message.
I have tried route.params, but it gives a warning that non-serializable values found.
React navigation warning
Also, I have heard that passing setState function to child component is very bad practice as it is mentioned here
I also tried navigation_events along with useEffect , this was a surprise to me that it didn't work either.
When I refresh the screen manually, or logout and log in, then it refreshes completely, but doesn't when I go back from application.
React.useEffect(() => {
navigation.addListener('focus', e => {
fetchConvos();
});
return () => {};
}, []); //also tried [navigation] instead of []
const fetchConvos = () => {
fetch('http://localhost:15000/' + id + '/conversations', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
})
.then(res => res.json())
.then(received => {
if (received?.response !== false) {
setConversations(received);
}
});
};
I have checked the id , received and even setConversations, they all are updating, but my screen still rendering the old messages.
Also, I don't want to use Async Storage or redux for this simple problem like this.
I can share the complete code if this isn't enough.
EDIT
I figured out one more way to update it may help clarify the situation more.
React.useEffect(() => {
navigation.addListener('focus', e => {
setConversations([]); //first setting convos to empty
fetchConvos(); //then fetching new data
});
return () => {};
}, []);
But this method is quite slow as I am updating the state of conversations twice.
I would appreciate if someone can help me here.
By taking the last 2 samples of code, I'd go down the route of setting the state to change the data. Like, I don't know the structure of your code completely. But i'm assuming you're using useEffect inside some component, right? In that case, React Context might be what you're looking for:
How to use react hooks on react-native with react-navigation
It allows to share informations, without having to build a structured store like redux. You should probably working on redesign a bit the code as, if you're following the current logic, you're going to split data pool of the conversation in the menu and load them when the "back" navigation event occurs, right?
Whilst the conversation data should be shared and available to both components, regardless where you're.
At least I'd rethink it this way to allow consistent data throughout the whole application.
Unless you've to do something specific and on-spot, of course.

Kick off separate redux-saga on login and logout

I'm learning Redux-Saga and having a bit of trouble wrapping my head round the correct flow for connectng people to a chat service (Chatkit by Pusher) when they log in and disconnecting them on logout.
So far I have an "auth" saga which waits for a LOGIN_REQUEST action, logs in to a REST api using axios then stores a username and token in the store by calling a USER_SET action.
My question is, when the login happens and the credentials are stored, should I PUT a new action called something like CHAT_CONNECT which would kick off another saga to connect to Chatkit, or should I get the chat saga to listen to the LOGIN_SUCCESS being fired and act on that? Is there even any practical difference in these two approaches.
As a bonus question, what's the best way to receive and act on new websocket messages from Chatkit using Redux Sagas? Here's the boilerplate code for connecting and receiving events from chatkit.
chatManager
.connect()
.then(currentUser => {
currentUser.subscribeToRoom({
roomId: currentUser.rooms[0].id,
hooks: {
onNewMessage: message => {
console.log(`Received new message: ${message.text}`)
}
}
});
})
.catch(error => {
console.error("error:", error);
})
Regarding your first question:
My question is, when the login happens and the credentials are stored, should I PUT a new action called something like CHAT_CONNECT which would kick off another saga to connect to Chatkit, or should I get the chat saga to listen to the LOGIN_SUCCESS being fired and act on that?
With the information provided its difficult to decide which approach is ideal because either will accomplish the same functionality. The biggest difference I see between the two proposed approaches is the direction of dependency. You have two different "modules" (features, packages, ...whatever you call your chunks of code that handle a single responsiblity), lets call them log-in and connect-chat.
If you dispatch an action CHAT_CONNECT from within the log-in saga, your log-in module will be dependent to the connect-chat module. Presumably, the connect-chat action will live in the connect-chat module.
Alternatively, if your connect-chat saga waits for LOGIN_SUCCESS, then your connect-chat module will be dependent on your log-in module. Presumably, the LOGIN_SUCCESS will live in the log-in module.
There's nothing wrong with either approach. Which is best depends on your applications needs and functionality.
If you might want to connect to chat any other time then after successfully logging in, then it might make sense to dispatch CHAT_CONNECT from within your log-in saga. Because chat is no longer dependent on log in. There are several scenarios where either approach will work better than the other, but it really depends on how the rest of your application is set up.
Regarding your bonus questions:
One approach to hooking external events in redux-saga is accomplished via eventChannels. Docs: https://redux-saga.js.org/docs/api/#eventchannelsubscribe-buffer-matcher
There's a bit of boiler plate, but I found this approach makes testing easier and truly encapsulates external functionality. Here's a quick example of how I might hook up an event channel to the code snippet you provided:
export const createOnMessageChannel = () =>
eventChannel((emit) => {
chatManager
.connect()
.then(currentUser => {
currentUser.subscribeToRoom({
roomId: currentUser.rooms[0].id,
hooks: {
onNewMessage: message => emit({ message }),
}
});
})
.catch(error => emit({ error }));
return () => {
// Code to unsubscribe, e.g. chatManager.disconnet() ?
};
});
export function* onMessage({ message, error }) {
if (error) {
yield put(handleError(error));
return;
}
yield put(handleMessage(message));
}
// this is what you pass to your root saga
export function* createOnMessageSaga() {
// using call because this makes it easier to test
const channel = yield call(createOnMessageChannel);
if (!channel) return;
yield takeEvery(channel, onMessage);
}

Resources